home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume13 / little-st2 / part02 < prev    next >
Encoding:
Internet Message Format  |  1988-01-30  |  46.9 KB

  1. Subject:  v13i054:  New release of little smalltalk, Part02/05
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Tim Budd <budd@MIST.CS.ORST.EDU>
  7. Posting-number: Volume 13, Issue 54
  8. Archive-name: little-st2/part02
  9.  
  10. #!/bin/sh
  11. #
  12. # This is version 2.02 of Little Smalltalk, distributed in five parts.
  13. # This version is dated 12/25/87
  14. # Several bugs and many features and improvements have been made since the
  15. # first posting to comp.src.unix.  See the file ``todo'' for a partial list.
  16. # Comments, bug reports, and the like should be submitted to:
  17. #     Tim Budd
  18. #     Smalltalk Distribution
  19. #     Department of Computer Science
  20. #     Oregon State University
  21. #     Corvallis, Oregon
  22. #     97330
  23. #     budd@cs.orst.edu
  24. #     {hp-pcd, tektronix}!orstcs!budd
  25. #
  26. echo 'Start of small.v2, part 02 of 05:'
  27. echo 'x - explore.ms'
  28. sed 's/^X//' > explore.ms << '/'
  29. X.SH
  30. XExploring and Creating
  31. X.PP
  32. XThis document describes how to discover information about existing objects 
  33. Xand create new objects using the Unix interface to the Little Smalltalk
  34. Xsystem (version two).  The Little Smalltalk system running 
  35. Xunder different operating
  36. Xsystems may have a slightly different interface, and the reader should be
  37. Xforewarned.
  38. X.PP
  39. XWhen you start version two Little Smalltalk under Unix, you will be given a 
  40. Xprompt.
  41. XYou can enter expressions in response to the prompt, and the system will 
  42. Xevaluate them (although it will not print the result unless you request 
  43. Xit\s-2\u*\d\s+2).
  44. X.FS
  45. X* Note that this is a change from version one of Little Smalltalk, where
  46. Xexpressions were automatically printed.
  47. XThe reason has to do with now expressions are compiled and executed
  48. Xnow, using more Smalltalk code, and less C code.
  49. X.FE
  50. X.DS I
  51. X>    (5 + 7) print
  52. X12
  53. X>
  54. X.DE
  55. XIn Smalltalk one communicates with objects by passing messages to them.
  56. XEven the addition sign shown above is treated as a message passed to the
  57. Xobject 5, with argument 7.  Other messages can be used to discover
  58. Xinformation about various objects.
  59. XThe most basic fact you can discover about an object is its class.
  60. XThis is given by the message \fBclass\fP, as in the following examples:
  61. X.DS I
  62. X>    7 class print
  63. XInteger
  64. X>    nil class print
  65. XUndefinedObject
  66. X.DE
  67. X.PP
  68. XOccasionally, especially when programming, one would like to ask whether
  69. Xthe class of an object matches some known class.  One way to do this would
  70. Xbe to use the message \fB=\!=\fP, which tells whether two expressions 
  71. Xrepresent the same object:
  72. X.DS I
  73. X>    ( 7 class =\!= Integer) print
  74. XTrue
  75. X>    nil class == Object ; print
  76. XFalse
  77. X.DE
  78. X.LP
  79. X(Notice that second example uses cascades in place of parenthesis.
  80. XThe only difference between these two is that in the first example the
  81. Xresult of the expression is the value returned by the print, whereas in the
  82. Xsecond the result of the expression is the value returned by =\!=.  But
  83. Xsince in any case the value is thrown away, it makes no difference.)
  84. X.PP
  85. XAn easier way is to use the message \fBisMemberOf:\fP;
  86. X.DS I
  87. X>    7 isMemberOf: Integer ; print
  88. XTrue
  89. X>    nil isMemberOf: Integer ; print
  90. XFalse
  91. X.DE
  92. X.PP
  93. XSometimes you want to know if an object is an instance of a particular
  94. Xclass or one if its subclasses; in this case the appropriate message is
  95. X\fBisKindOf:\fP.
  96. X.DS I
  97. X>    7 isMemberOf: Number ; print
  98. XFalse
  99. X>    7 isKindOf: Number ; print
  100. XTrue
  101. X.DE
  102. X.PP
  103. XAll objects will respond to the message \fBdisplay\fP by telling a little
  104. Xabout themselves.  Many just give their class and their printable
  105. Xrepresentation:
  106. X.DS I
  107. X>    7 display
  108. X(Class Integer) 7
  109. X>    nil display
  110. X(Class UndefinedObject) nil
  111. X.DE
  112. X.LP
  113. XOthers, such as classes, are a little more verbose:
  114. X.DS I
  115. X>    Integer display
  116. XClass Name: Integer
  117. XSuperClass: Number
  118. XInstance Variables:
  119. Xno instance variables
  120. XSubclasses:
  121. X.DE
  122. X.LP
  123. XThe display shows that the class \fBInteger\fP is a subclass of class
  124. X\fBNumber\fP (that is, class \fBNumber\fP is the superclass of
  125. X\fBInteger\fP).  There are no instance variables for this class, and it
  126. Xcurrently has no subclasses.  
  127. XAll of this information could be obtained by means of other messages,
  128. Xalthough the \fBdisplay\fP form is the easiest.
  129. X.DS I
  130. X>    List variables display
  131. Xlinks
  132. X>    Integer superClass print
  133. XNumber
  134. X>    Collection subClasses display
  135. XIndexedCollection
  136. XInterval
  137. XList
  138. X.DE
  139. XAbout the only bit of information that is not provided when one passes the
  140. Xmessage \fBdisplay\fP to a class
  141. Xis a list of methods the class responds to.  There are two
  142. Xreasons for this omission; the first is that this list can often be quite
  143. Xlong, and we don't want to scroll the other information off the screen
  144. Xbefore the user has seen it.  The second reason is that there are really
  145. Xtwo different questions the user could be asking.  The first is what
  146. Xmethods are actually implemented in a given class.  A dictionary containing
  147. Xthe set of methods implemented in a class can be found by passing the
  148. Xmessage \fBmethods\fP to a class.  Since we are only interested in the set
  149. Xof keys for this dictionary (that is, the message selectors), we can use
  150. Xthe message \fBkeys\fP.  Finally, as we saw with the message
  151. X\fBsubClasses\fP shown above, our old friend \fBdisplay\fP prints this
  152. Xinformation out one method to a line:
  153. X.DS I
  154. X>    True methods keys display
  155. X#ifTrue:ifFalse:
  156. X#not
  157. X.DE
  158. X.PP
  159. XA second question that one could ask is what message selectors an instance of a
  160. Xgiven class will respond to, whether they are inherited from superclasses
  161. Xor are defined in the given class.  This set is given in response to the
  162. Xmessage \fBrespondsTo\fP.
  163. X.DS I
  164. X>    True respondsTo display
  165. X#class
  166. X#==
  167. X#hash
  168. X#isNil
  169. X#display
  170. X#=
  171. X#basicSize
  172. X#isMemberOf:
  173. X#notNil
  174. X#print
  175. X#basicAt:put:
  176. X#isKindOf:
  177. X#basicAt:
  178. X#printString
  179. X#or:
  180. X#and:
  181. X#ifFalse:ifTrue:
  182. X#ifTrue:
  183. X#ifFalse:
  184. X#not
  185. X#ifTrue:ifFalse:
  186. X.DE
  187. X.PP
  188. XAlternatively, one can ask whether instances of a given class will respond
  189. Xto a specific message by writing the message selector as a symbol:
  190. X.DS I
  191. X>    ( String respondsTo: #print ) print
  192. XTrue
  193. X>    String respondsTo: #+ ; print
  194. XFalse
  195. X.DE
  196. X.PP
  197. XThe inverse of this would be to ask what classes contain methods for a
  198. Xgiven message selector.  Class \fBSymbol\fP defines a method to yield just
  199. Xthis information:
  200. X.DS I
  201. X>    #+ respondsTo display
  202. XInteger
  203. XNumber
  204. XFloat
  205. X.DE
  206. X.PP
  207. XThe method that will be executed in response to a given message selector
  208. Xcan be displayed by means of the message \fBviewMethod:\fP
  209. X.DS I
  210. X>    Integer viewMethod: #gcd:
  211. Xgcd: value
  212. X    (value = 0) ifTrue: [ \(ua self ].
  213. X    (self negative) ifTrue: [ \(ua self negated gcd: value ].
  214. X    (value negative) ifTrue: [ \(ua self gcd: value negated ].
  215. X    (value > self) ifTrue: [ \(ua value gcd: self ].
  216. X    \(ua value gcd: (self rem: value)
  217. X.DE
  218. X.PP
  219. XNew functionality can be added using the message \fBaddMethod\fP.
  220. XWhen passed to an instance of \fBClass\fP, this message drops the user into
  221. Xa standard Unix Editor.  A body for a new method can then be entered.
  222. XWhen the user exists the editor, the method body is compiled.  If it is
  223. Xsyntactically correct, it is added to the methods for the class.  If it is
  224. Xincorrect, the user is given the option of re-editing the method.
  225. X.DS I
  226. X>    Integer addMethod
  227. X\& ... drop into editor and enter the following text
  228. X% x
  229. X    \(ua ( x + )
  230. X\& ... exit editor
  231. Xcompiler error: invalid expression start )
  232. Xedit again (yn) ?
  233. X\& ...
  234. X.DE
  235. X.PP
  236. XIn a similar manner, existing methods can be editing by passing their
  237. Xselectors, as symbols to the message \fBeditMethod:\fP.
  238. X.DS I
  239. X>    Integer editMethod: #gcd:
  240. X\& ... drop into editor working on the body of gcd:
  241. X.DE
  242. X.PP
  243. XThe name of the editor used by these methods is taken from a string
  244. Xpointed to by the global variable \fIeditor\fP.  Different editors can be
  245. Xselected merely by redefining this value:
  246. X.DS I
  247. XglobalNames at: #editor put: 'emacs'
  248. X.DE
  249. X.PP
  250. XSome Smalltalk systems make it very difficult for you to discover the
  251. Xbytecodes that a method gets translated into.  Since the primary goal of
  252. XLittle Smalltalk is to help the student to discover how a modern very high
  253. Xleval language is implemented, it makes sense that the system should help
  254. Xyou as much as possible discover everything about its internal structure.
  255. XThus a method, when presented with the message \fBdisplay\fP, will print
  256. Xout its bytecode representation.
  257. X.DS I
  258. X>    Char methods at: #isAlphabetic ; display
  259. XMethod #isAlphabetic
  260. X    isAlphabetic
  261. X        ^ (self isLowercase) or: [ self isUppercase ]
  262. X
  263. Xliterals
  264. XArray ( #isLowercase #isUppercase )
  265. Xbytecodes
  266. X32 2 0
  267. X144 9 0
  268. X0 0 0
  269. X250 15 10
  270. X8 0 8
  271. X32 2 0
  272. X144 9 0
  273. X1 0 1
  274. X242 15 2
  275. X241 15 1
  276. X.DE
  277. X.PP
  278. XBytecodes are represented by four bit opcodes and four bit operands, with
  279. Xoccasional bytes representing data (more detail can be found in the book).
  280. XThe three numbers written on each line for the bytecodes represent the 
  281. Xbyte value followed by the upper four bits and the lower four bits.
  282. X.PP
  283. XNew objects are created using the message \fBnew\fP.  
  284. XWithin a method
  285. Xthese can be assigned to instance varibles using the assignment arrow.
  286. X.DS I
  287. X\fBaMethod\fP
  288. X    x \(<- Set new.
  289. X    \&...
  290. X.DE
  291. X.PP
  292. XThe assignment arrow is not recognized at the topmost level.  Instead,
  293. Xglobal variables (variables recognized in any context), are created by
  294. Xpassing messages to \fBglobalNames\fP (below).
  295. X.PP
  296. XNew classes, on the
  297. Xother hand, are created by sending a message \fBaddSubClass\fP to the class
  298. Xthat will be the superclass of the new class.  The user will then be
  299. Xinterrogated for information to be associated with the new class:
  300. X.DS I
  301. X>    Object addSubClass
  302. XClass Name? Foo
  303. XInstance Variables? x y z
  304. XAdd a method (yn) ? y
  305. X\&...
  306. X>    Foo display
  307. XClass Name: Foo
  308. XSuperclass: Object
  309. XInstance Variables: 
  310. Xx
  311. Xy
  312. Xz
  313. XSubclasses:
  314. X.DE
  315. X.PP
  316. XClasses created using \fBaddSubClass\fP will be automatically added to the
  317. Xlist of global variables.  Other global variables can be created merely by
  318. Xplacing their name and value into the 
  319. Xdictionary \fBglobalNames\fP\s-2\u*\d\s+2.
  320. X.DS I
  321. X>    globalNames at: #version put: 2.1
  322. X
  323. X>    version print
  324. X2.1
  325. X.DE
  326. X.FS
  327. X* This is a change from version 1 of Little Smalltalk, where it was
  328. Xpossible to create global variables merely by assiging a value to them at
  329. Xthe command level.  The change is an unfortunate consequence of the
  330. Xfact that more is done now
  331. Xis Smalltalk, and less in C.  The bytecode interpreter now knows little
  332. Xabout the object globalNames, in particular, the bytecode interpreter
  333. Xdoesn't know how to add a new object; this is done entirely in Smalltalk
  334. Xcode.  One possiblity would be to automatically have the parser change an
  335. Xassignment at the command level into an at:put:, but this would seem to
  336. Xcomplicate the parser unnecessarily.
  337. X.FE
  338. X.PP
  339. XIf you have written a new class and want to print the class methods on a
  340. Xfile you can use the message \fBfileOut:\fP, after first creating a file to
  341. Xwrite to.  Both classes and individual methods can be filed out, and
  342. Xseveral classes and/or methods can be placed in one file.
  343. X.DS I
  344. X>    globalNames at: #f put: File new
  345. X>    f name: 'foo.st'
  346. X>    f open: 'w'
  347. X>    Foo fileOut: f
  348. X>    Bar fileOut: f
  349. X>    Object fileOutMethod: #isFoo to: f
  350. X>    f close
  351. X.DE
  352. X.LP
  353. XThe file ``newfile'' will now have a printable representation of the
  354. Xmethods for the class Foo.
  355. XThese can subsequently be filed back into a different smalltalk image.
  356. X.DS I
  357. X>    globalNames at: #f put: File new
  358. X>    f name: 'foo.st'
  359. X>    f open: 'r'
  360. X>    f fileIn
  361. X>    2 isFoo print
  362. XFalse
  363. X.DE
  364. X.PP
  365. XFinally, once the user has added classes and variables and made whatever other
  366. Xchanges they want, the message \fBsaveImage\fP, passed to the pseudo
  367. Xvariable \fBsmalltalk\fP, can be used to save an entire object image on a file.
  368. XIf the writing of the image is successful, a message will be displayed.
  369. X.DS I
  370. X>    smalltalk saveImage
  371. XImage name? newimage
  372. Ximage newimage created
  373. X>    
  374. X.DE
  375. X.PP
  376. XTyping control-D causes the interpreter to exit.
  377. X.PP
  378. XWhen the smalltalk system is restarted, an alternative image, such as the
  379. Ximage just created, can be specified by giving its name on the argument
  380. Xline:
  381. X.DS I
  382. Xst newimage
  383. X.DE
  384. X.PP
  385. XFurther information on Little Smalltalk can be found in the book.
  386. X.SH
  387. XIncompatabilities with the Book
  388. X.PP
  389. XIt is unfortunately the case that during the transition from version 1 (the
  390. Xversion described in the book) and version 2 (the new version that is one
  391. Xthird the size and three times faster), certain changes to the user
  392. Xinterface were required.  I will describe these here.
  393. X.PP
  394. XThe first incompatability comes at the very beginning.  In version 1 there
  395. Xwere a great number of command line options.  These have all been
  396. Xeliminated in version two.  In version two the only command line option is
  397. Xthe file name of an image file.
  398. X.PP
  399. XIn version 1 it is possible to create global variables simply by assigning
  400. Xto them.  That is, a statement such as
  401. X.DS I
  402. Xxx \(<- 27
  403. X.DE
  404. Xwhen issued at the command level would create a new global variable.
  405. XSince it is not possible to assign to an unknown name within a method, this
  406. Xin effect required the version one system to keep around two parsers, one
  407. Xfor methods and another for command lines.  These were replaced with a
  408. Xsingle parser in version two, which necessitated a change.  Now to create a
  409. Xglobal variable one must first establish it in the dictionary, using the
  410. Xcommand 
  411. X.DS I
  412. XglobalNames at: #xx put: 27
  413. X.DE
  414. XIt is not possible to use assignment to create a global variable in version
  415. Xtwo.
  416. X.PP
  417. XThe interface to the editor has been changed.  In version one this was
  418. Xhandled by the system, and not by Smalltalk code.  This required a command
  419. Xformat that was clearly not a Smalltalk command, so that they could be
  420. Xdistinguished.  The convention adoped was to use an APL style system
  421. Xcommand:
  422. X.DS I
  423. X)e filename
  424. X.DE
  425. XIn version two we have moved these functions into Smalltalk code.  Now
  426. Xthe problem is just the reverse, we need a command that is a Smalltalk
  427. Xcommand.  In addition, in version one entire classes were edited at once,
  428. Xwhereas in version two only individual methods are edited.  As we have
  429. Xalready noted, the new commands to add or edit methods are as follows:
  430. X.DS I
  431. X\fIclassname\fP addMethod
  432. X\fIclassname\fP editMethod: \fImethodname\fP
  433. X.DE
  434. X.PP
  435. XThe only other significant syntactic change is the way primitive methods
  436. Xare invoked.  In version one these were either named or numbered, 
  437. Xsomething like the following:
  438. X.DS I
  439. X<primitive 37 a b>
  440. X<IntegerAdd a b>
  441. X.DE
  442. XIn version two we have simply eliminated the keyword \fBprimitive\fP, so
  443. Xprimitives now look like:
  444. X.DS I
  445. X<37 a b>
  446. X.DE
  447. X.PP
  448. XThere are far fewer primitives in version two, and much more of the system
  449. Xis now performed using Smalltalk code.
  450. X.PP
  451. XIn addition to these syntactic changes, there are various small changes in
  452. Xthe class structure.  I hope to have a document describing these changes at
  453. Xsome point, but as of right now the code itself is the best description.
  454. /
  455. echo 'x - image.c'
  456. sed 's/^X//' > image.c << '/'
  457. X/*
  458. X    Little Smalltalk, version 2
  459. X    Written by Tim Budd, Oregon State University, July 1987
  460. X
  461. X    routines used in the making of the initial object image
  462. X*/
  463. X
  464. X# include <stdio.h>
  465. X# include "env.h"
  466. X# include "memory.h"
  467. X# include "names.h"
  468. X# include "lex.h"
  469. X# ifdef STRING
  470. X# include <string.h>
  471. X# endif
  472. X# ifdef STRINGS
  473. X# include <strings.h>
  474. X# endif
  475. X
  476. X# define SymbolTableSize 71
  477. X# define GlobalNameTableSize 53
  478. X# define MethodTableSize 39
  479. X
  480. X# define globalNameSet(sym, value) nameTableInsert(globalNames, sym, value)
  481. X/*
  482. X    the following classes are used repeately, so we put them in globals.
  483. X*/
  484. Xstatic object ObjectClass;
  485. Xstatic object ClassClass;
  486. Xstatic object LinkClass;
  487. Xstatic object DictionaryClass;
  488. Xstatic object ArrayClass;
  489. X
  490. X/*
  491. X    we read the input a line at a time, putting lines into the following
  492. X    buffer.  In addition, all methods must also fit into this buffer.
  493. X*/
  494. X# define TextBufferSize 1024
  495. Xstatic char textBuffer[TextBufferSize];
  496. X
  497. X/*
  498. X    nameTableInsert is used to insert a symbol into a name table.
  499. X    see names.c for futher information on name tables
  500. X*/
  501. XnameTableInsert(dict, symbol, value)
  502. Xobject dict, symbol, value;
  503. X{    object table, link, newLink, nextLink, tablentry;
  504. X    int hash;
  505. X
  506. X    /* first get the hash table */
  507. X    table = basicAt(dict, 1);
  508. X
  509. X    if (objectSize(table) < 3)
  510. X        sysError("attempt to insert into","too small name table");
  511. X    else {
  512. X        hash = 3 * ( symbol % (objectSize(table) / 3));
  513. X        tablentry = basicAt(table, hash+1);
  514. X        if ((tablentry == nilobj) || (tablentry == symbol)) {
  515. X            basicAtPut(table, hash+1, symbol);
  516. X            basicAtPut(table, hash+2, value);
  517. X            }
  518. X        else {
  519. X            newLink = allocObject(3);
  520. X            incr(newLink);
  521. X            setClass(newLink, globalSymbol("Link"));
  522. X            basicAtPut(newLink, 1, symbol);
  523. X            basicAtPut(newLink, 2, value);
  524. X            link = basicAt(table, hash+3);
  525. X            if (link == nilobj)
  526. X                basicAtPut(table, hash+3, newLink);
  527. X            else
  528. X                while(1)
  529. X                    if (basicAt(link,1) == symbol) {
  530. X                        basicAtPut(link, 2, value);
  531. X                        break;
  532. X                        }
  533. X                    else if ((nextLink = basicAt(link, 3)) == nilobj) {
  534. X                        basicAtPut(link, 3, newLink);
  535. X                        break;
  536. X                        }
  537. X                    else
  538. X                        link = nextLink;
  539. X            decr(newLink);
  540. X            }
  541. X    }
  542. X}
  543. X
  544. X/*
  545. X    there is sort of a chicken and egg problem about building the 
  546. X    first classes.
  547. X    in order to do it, you need symbols, 
  548. X    but in order to make symbols, you need the class Symbol.
  549. X    the routines makeClass and buildInitialNameTable attempt to get 
  550. X    carefully get around this initialization problem
  551. X*/
  552. X
  553. Xstatic object makeClass(name)
  554. Xchar *name;
  555. X{    object theClass, theSymbol;
  556. X
  557. X    /* this can only be called once newSymbol works properly */
  558. X
  559. X    theClass = allocObject(classSize);
  560. X    theSymbol = newSymbol(name);
  561. X    basicAtPut(theClass, nameInClass, theSymbol);
  562. X    globalNameSet(theSymbol, theClass);
  563. X    setClass(theClass, ClassClass);
  564. X
  565. X    return(theClass);
  566. X}
  567. X
  568. XbuildInitialNameTables()
  569. X{    object symbolString, classString;
  570. X    object globalHashTable;
  571. X    int hash;
  572. X    char *p;
  573. X
  574. X    /* build the table that contains all symbols */
  575. X    symbols = allocObject(2 * SymbolTableSize);
  576. X    incr(symbols);
  577. X
  578. X    /* build the table (a dictionary) that contains all global names */
  579. X    globalNames = allocObject(1);
  580. X    globalHashTable = allocObject(3 * GlobalNameTableSize);
  581. X    incr(globalNames);
  582. X    basicAtPut(globalNames, 1, globalHashTable);
  583. X
  584. X    /* next create class Symbol, so we can call newSymbol */
  585. X    /* notice newSymbol uses the global variable symbolclass */
  586. X    symbolString = allocSymbol("Symbol");
  587. X    symbolclass =  allocObject(classSize);
  588. X    setClass(symbolString, symbolclass);
  589. X    basicAtPut(symbolclass, nameInClass, symbolString);
  590. X    /* we recreate the hash computation used by newSymbol */
  591. X    hash = 0;
  592. X    for (p = "Symbol"; *p; p++)
  593. X        hash += *p;
  594. X    if (hash < 0) hash = - hash;
  595. X    hash %= (objectSize(symbols) / 2);
  596. X    basicAtPut(symbols, 2*hash + 1, symbolString);
  597. X    globalNameSet(symbolString, symbolclass);
  598. X    /* now the routine newSymbol should work properly */
  599. X
  600. X    /* now go on to make class Class so we can call makeClass */
  601. X    ClassClass = allocObject(classSize);
  602. X    classString = newSymbol("Class");
  603. X    basicAtPut(ClassClass, nameInClass, classString);
  604. X    globalNameSet(classString, ClassClass);
  605. X    setClass(ClassClass, ClassClass);
  606. X    setClass(symbolclass, ClassClass);
  607. X
  608. X    /* now create a few other important classes */
  609. X    ObjectClass = makeClass("Object");
  610. X    LinkClass = makeClass("Link");
  611. X    setClass(nilobj, makeClass("UndefinedObject"));
  612. X    DictionaryClass = makeClass("Dictionary");
  613. X    ArrayClass = makeClass("Array");
  614. X    setClass(symbols, DictionaryClass);
  615. X    setClass(globalNames, DictionaryClass);
  616. X    setClass(globalHashTable, ArrayClass);
  617. X    
  618. X}
  619. X
  620. X/*
  621. X    findClass gets a class object,
  622. X    either by finding it already or making it
  623. X    in addition, it makes sure it has a size, by setting
  624. X    the size to zero if it is nil.
  625. X*/
  626. Xstatic object findClass(name)
  627. Xchar *name;
  628. X{    object newobj;
  629. X
  630. X    newobj = globalSymbol(name);
  631. X    if (newobj == nilobj)
  632. X        newobj = makeClass(name);
  633. X    if (basicAt(newobj, sizeInClass) == nilobj)
  634. X        basicAtPut(newobj, sizeInClass, newInteger(0));
  635. X    return(newobj);
  636. X}
  637. X
  638. X/*
  639. X    readDeclaration reads a declaration of a class
  640. X*/
  641. Xstatic readDeclaration()
  642. X{    object classObj, super, vars;
  643. X    int i, size, instanceTop;
  644. X    object instanceVariables[15];
  645. X
  646. X    if (nextToken() != nameconst)
  647. X        sysError("bad file format","no name in declaration");
  648. X    classObj = findClass(tokenString);
  649. X    size = 0;
  650. X    if (nextToken() == nameconst) {    /* read superclass name */
  651. X        super = findClass(tokenString);
  652. X        basicAtPut(classObj, superClassInClass, super);
  653. X        size = intValue(basicAt(super, sizeInClass));
  654. X        ignore nextToken();
  655. X        }
  656. X    if (token == nameconst) {        /* read instance var names */
  657. X        instanceTop = 0;
  658. X        while (token == nameconst) {
  659. X            instanceVariables[instanceTop++] = newSymbol(tokenString);
  660. X            size++;
  661. X            ignore nextToken();
  662. X            }
  663. X        vars = newArray(instanceTop);
  664. X        for (i = 0; i < instanceTop; i++)
  665. X            basicAtPut(vars, i+1, instanceVariables[i]);
  666. X        basicAtPut(classObj, variablesInClass, vars);
  667. X        }
  668. X    basicAtPut(classObj, sizeInClass, newInteger(size));
  669. X}
  670. X
  671. X/*
  672. X    readInstance - read an instance directive 
  673. X*/
  674. Xstatic readInstance()
  675. X{    object classObj, newObj;
  676. X    int size;
  677. X
  678. X    if (nextToken() != nameconst)
  679. X        sysError("no name","following instance command");
  680. X    classObj = globalSymbol(tokenString);
  681. X    if (nextToken() != nameconst)
  682. X        sysError("no instance name","in instance command");
  683. X
  684. X    /* now make a new instance of the class -
  685. X        note that we can't do any initialization */
  686. X    size = intValue(basicAt(classObj, sizeInClass));
  687. X    newObj = allocObject(size);
  688. X    setClass(newObj, classObj);
  689. X    globalNameSet(newSymbol(tokenString), newObj);
  690. X}
  691. X
  692. X/*
  693. X    readClass reads a class method description
  694. X*/
  695. Xstatic readClass(fd, printit)
  696. XFILE *fd;
  697. Xboolean printit;
  698. X{    object classObj, methTable, theMethod, selector;
  699. X# define LINEBUFFERSIZE 512
  700. X    object methDict;
  701. X    char *eoftest, lineBuffer[LINEBUFFERSIZE];
  702. X
  703. X    /* if we haven't done it already, read symbols now */
  704. X    if (trueobj == nilobj)
  705. X        initCommonSymbols();
  706. X
  707. X    if (nextToken() != nameconst)
  708. X        sysError("missing name","following Class keyword");
  709. X    classObj = findClass(tokenString);
  710. X    setInstanceVariables(classObj);
  711. X    if (printit)
  712. Xignore fprintf(stderr,"class %s\n", charPtr(basicAt(classObj, nameInClass)));
  713. X
  714. X    /* find or create a methods table */
  715. X    methTable = basicAt(classObj, methodsInClass);
  716. X    if (methTable == nilobj) {
  717. X        methTable = allocObject(1);
  718. X        basicAtPut(classObj, methodsInClass, methTable);
  719. X        setClass(methTable, globalSymbol("Dictionary"));
  720. X        methDict = allocObject(MethodTableSize);
  721. X        basicAtPut(methTable, 1, methDict);
  722. X        setClass(methDict, globalSymbol("Array"));
  723. X        }
  724. X
  725. X    /* now go read the methods */
  726. X    do {
  727. X        textBuffer[0] = '\0';
  728. X        while((eoftest = fgets(lineBuffer, LINEBUFFERSIZE, fd)) != NULL) {
  729. X            if ((lineBuffer[0] == '|') || (lineBuffer[0] == ']'))
  730. X                break;
  731. X            ignore strcat(textBuffer, lineBuffer);
  732. X            }
  733. X        if (eoftest == NULL) {
  734. X            sysError("unexpected end of file","while reading method");
  735. X            break;
  736. X            }
  737. X        /* now we have a method */
  738. X        theMethod = allocObject(methodSize);
  739. X        setClass(theMethod, globalSymbol("Method"));
  740. X        if (parse(theMethod, textBuffer)) {
  741. X            selector = basicAt(theMethod, messageInMethod);
  742. X            if (printit)
  743. Xignore fprintf(stderr,"method %s\n", charPtr(selector));
  744. X            nameTableInsert(methTable, selector, theMethod);
  745. X            }
  746. X        else {
  747. X            /* get rid of unwanted method */
  748. X            incr(theMethod);
  749. X            decr(theMethod);
  750. Xignore fprintf(stderr,"push return to continue\n");
  751. Xignore gets(textBuffer);
  752. X            }
  753. X        
  754. X    } while (lineBuffer[0] != ']');
  755. X}
  756. X
  757. X/*
  758. X    readFile reads a class descriptions file
  759. X*/
  760. XreadFile(fd, printit)
  761. XFILE *fd;
  762. Xboolean printit;
  763. X{
  764. X    while(fgets(textBuffer, TextBufferSize, fd) != NULL) {
  765. X        lexinit(textBuffer);
  766. X        if (token == inputend)
  767. X            ; /* do nothing, get next line */
  768. X        else if ((token == binary) && streq(tokenString, "*"))
  769. X            ; /* do nothing, its a comment */
  770. X        else if ((token == nameconst) && streq(tokenString, "Declare"))
  771. X            readDeclaration();
  772. X        else if ((token == nameconst) && streq(tokenString,"Instance"))
  773. X            readInstance();
  774. X        else if ((token == nameconst) && streq(tokenString,"Class"))
  775. X            readClass(fd, printit);
  776. X        else 
  777. X            ignore fprintf(stderr,"unknown line %s\n", textBuffer);
  778. X        }
  779. X}
  780. /
  781. echo 'x - parser.c'
  782. sed 's/^X//' > parser.c << '/'
  783. X/*
  784. X    Little Smalltalk, version 2
  785. X    Written by Tim Budd, Oregon State University, July 1987
  786. X
  787. X    Method parser - parses the textual description of a method,
  788. X    generating bytecodes and literals.
  789. X
  790. X    This parser is based around a simple minded recursive descent
  791. X    parser.
  792. X    It is used both by the module that builds the initial virtual image,
  793. X    and by a primitive when invoked from a running Smalltalk system.
  794. X
  795. X    The latter case could, if the bytecode interpreter were fast enough,
  796. X    be replaced by a parser written in Smalltalk.  This would be preferable,
  797. X    but not if it slowed down the system too terribly.
  798. X
  799. X    To use the parser the routine setInstanceVariables must first be
  800. X    called with a class object.  This places the appropriate instance
  801. X    variables into the memory buffers, so that references to them
  802. X    can be correctly encoded.
  803. X
  804. X    As this is recursive descent, you should read it SDRAWKCAB !
  805. X        (from bottom to top)
  806. X*/
  807. X# include <stdio.h>
  808. X# include "env.h"
  809. X# include "memory.h"
  810. X# include "names.h"
  811. X# include "interp.h"
  812. X# include "lex.h"
  813. X# ifdef STRING
  814. X# include <string.h>
  815. X# endif
  816. X# ifdef STRINGS
  817. X# include <strings.h>
  818. X# endif
  819. X
  820. X        /* all of the following limits could be increased (up to
  821. X            256) without any trouble.  They are kept low 
  822. X            to keep memory utilization down */
  823. X
  824. X# define codeLimit 256        /* maximum number of bytecodes permitted */
  825. X# define literalLimit 32    /* maximum number of literals permitted */
  826. X# define temporaryLimit 16    /* maximum number of temporaries permitted */
  827. X# define argumentLimit 16    /* maximum number of arguments permitted */
  828. X# define instanceLimit 16    /* maximum number of instance vars permitted */
  829. X# define methodLimit 32        /* maximum number of methods permitted */
  830. X
  831. Xextern object binSyms[];
  832. Xextern object keySyms[];
  833. Xextern char *unStrs[], *binStrs[], *keyStrs[];
  834. X
  835. Xstatic boolean parseok;            /* parse still ok? */
  836. Xstatic int codeTop;            /* top position filled in code array */
  837. Xstatic byte codeArray[codeLimit];    /* bytecode array */
  838. Xstatic int literalTop;            /*  ... etc. */
  839. Xstatic object literalArray[literalLimit];
  840. Xstatic int temporaryTop;
  841. Xstatic char *temporaryName[temporaryLimit];
  842. Xstatic int argumentTop;
  843. Xstatic char *argumentName[argumentLimit];
  844. Xstatic int instanceTop;
  845. Xstatic char *instanceName[instanceLimit];
  846. X
  847. Xstatic int maxTemporary;        /* highest temporary see so far */
  848. Xstatic char selector[80];        /* message selector */
  849. X
  850. Xstatic boolean inBlock;            /* true if compiling a block */
  851. Xstatic boolean optimizedBlock;        /* true if compiling optimized block */
  852. X
  853. XsetInstanceVariables(aClass)
  854. Xobject aClass;
  855. X{    int i, limit;
  856. X    object vars;
  857. X
  858. X    if (aClass == nilobj)
  859. X        instanceTop = 0;
  860. X    else {
  861. X        setInstanceVariables(basicAt(aClass, superClassInClass));
  862. X        vars = basicAt(aClass, variablesInClass);
  863. X        if (vars != nilobj) {
  864. X            limit = objectSize(vars);
  865. X            for (i = 1; i <= limit; i++)
  866. X                instanceName[++instanceTop] = charPtr(basicAt(vars, i));
  867. X            }
  868. X        }
  869. X}
  870. X
  871. XcompilWarn(str1, str2)
  872. Xchar *str1, *str2;
  873. X{
  874. X    ignore fprintf(stderr,"compiler warning: %s %s\n", str1, str2);
  875. X}
  876. X
  877. XcompilError(str1, str2)
  878. Xchar *str1, *str2;
  879. X{
  880. X    ignore fprintf(stderr,"compiler error: %s %s\n", str1, str2);
  881. X    parseok = false;
  882. X}
  883. X
  884. Xstatic object newChar(value)
  885. Xint value;
  886. X{    object newobj;
  887. X
  888. X    newobj = allocObject(1);
  889. X    basicAtPut(newobj, 1, newInteger(value));
  890. X    setClass(newobj, globalSymbol("Char"));
  891. X    return(newobj);
  892. X}
  893. X
  894. Xstatic object newByteArray(size)
  895. Xint size;
  896. X{    object newobj;
  897. X
  898. X    newobj = allocByte(size);
  899. X    setClass(newobj, globalSymbol("ByteArray"));
  900. X    return(newobj);
  901. X}
  902. X
  903. Xstatic genCode(value)
  904. Xint value;
  905. X{
  906. X    if (codeTop >= codeLimit)
  907. X        compilError("too many bytecode instructions in method","");
  908. X    else
  909. X        codeArray[codeTop++] = value;
  910. X}
  911. X
  912. Xstatic genInstruction(high, low)
  913. Xint high, low;
  914. X{
  915. X    if (low >= 16) {
  916. X        genInstruction(0, high);
  917. X        genCode(low);
  918. X        }
  919. X    else
  920. X        genCode(high * 16 + low);
  921. X}
  922. X
  923. Xstatic int genLiteral(aLiteral)
  924. Xobject aLiteral;
  925. X{
  926. X    if (literalTop >= literalLimit)
  927. X        compilError("too many literals in method","");
  928. X    else {
  929. X        literalArray[++literalTop] = aLiteral;
  930. X        incr(aLiteral);
  931. X        }
  932. X    return(literalTop - 1);
  933. X}
  934. X
  935. Xstatic char *glbsyms[] = {"nil", "true", "false", "smalltalk", "globalNames",
  936. X0 };
  937. X
  938. Xstatic boolean nameTerm(name)
  939. Xchar *name;
  940. X{    int i;
  941. X    boolean done = false;
  942. X    boolean isSuper = false;
  943. X    object newterm;
  944. X
  945. X    /* it might be self or super */
  946. X    if (streq(name, "self") || streq(name, "super")) {
  947. X        genInstruction(PushArgument, 0);
  948. X        done = true;
  949. X        if (streq(name,"super")) isSuper = true;
  950. X        }
  951. X
  952. X    /* or it might be a temporary */
  953. X    if (! done)
  954. X        for (i = 1; (! done) && ( i <= temporaryTop ) ; i++)
  955. X            if (streq(name, temporaryName[i])) {
  956. X                genInstruction(PushTemporary, i-1);
  957. X                done = true;
  958. X                }
  959. X
  960. X    /* or it might be an argument */
  961. X    if (! done)
  962. X        for (i = 1; (! done) && (i <= argumentTop ) ; i++)
  963. X            if (streq(name, argumentName[i])) {
  964. X                genInstruction(PushArgument, i);
  965. X                done = true;
  966. X                }
  967. X
  968. X    /* or it might be an instance variable */
  969. X    if (! done)
  970. X        for (i = 1; (! done) && (i <= instanceTop); i++) {
  971. X            if (streq(name, instanceName[i])) {
  972. X                genInstruction(PushInstance, i-1);
  973. X                done = true;
  974. X                }
  975. X            }
  976. X
  977. X    /* or it might be a global constant */
  978. X    if (! done)
  979. X        for (i = 0; (! done) && glbsyms[i]; i++)
  980. X            if (streq(name, glbsyms[i])) {
  981. X                genInstruction(PushConstant, i+4);
  982. X                done = true;
  983. X                }
  984. X
  985. X    /* not anything else, it must be a global */
  986. X    /* see if we know of it first */
  987. X    if (! done) { 
  988. X        newterm = globalSymbol(name);
  989. X        if (newterm != nilobj) {
  990. X            genInstruction(PushLiteral, genLiteral(newterm));
  991. X            done = true;
  992. X            }
  993. X        }
  994. X
  995. X    /* otherwise, must look it up at run time */
  996. X    if (! done) {
  997. X        genInstruction(PushGlobal, genLiteral(newSymbol(name)));
  998. X        }
  999. X
  1000. X    return(isSuper);
  1001. X}
  1002. X
  1003. Xstatic int parseArray()
  1004. X{    int i, size, base;
  1005. X    object newLit, obj;
  1006. X
  1007. X    base = literalTop;
  1008. X    ignore nextToken();
  1009. X    while (parseok && (token != closing)) {
  1010. X        switch(token) {
  1011. X            case arraybegin:
  1012. X                ignore parseArray();
  1013. X                break;
  1014. X
  1015. X            case intconst:
  1016. X                ignore genLiteral(newInteger(tokenInteger));
  1017. X                ignore nextToken();
  1018. X                break;
  1019. X
  1020. X            case floatconst:
  1021. X                ignore genLiteral(newFloat(tokenFloat));
  1022. X                ignore nextToken();
  1023. X                break;
  1024. X
  1025. X            case nameconst: case namecolon: case symconst:
  1026. X                ignore genLiteral(newSymbol(tokenString));
  1027. X                ignore nextToken();
  1028. X                break;
  1029. X
  1030. X            case binary:
  1031. X                if (streq(tokenString, "(")) {
  1032. X                    ignore parseArray();
  1033. X                    }
  1034. X                else {
  1035. X                    ignore genLiteral(newSymbol(tokenString));
  1036. X                    ignore nextToken();
  1037. X                    }
  1038. X                break;
  1039. X
  1040. X            case charconst:
  1041. X                ignore genLiteral(newChar(
  1042. X                    newInteger(tokenInteger)));
  1043. X                ignore nextToken();
  1044. X                break;
  1045. X
  1046. X            case strconst:
  1047. X                ignore genLiteral(newStString(tokenString));
  1048. X                ignore nextToken();
  1049. X                break;
  1050. X
  1051. X            default:
  1052. X                compilError("illegal text in literal array",
  1053. X                    tokenString);
  1054. X                ignore nextToken();
  1055. X                break;
  1056. X        }
  1057. X    }
  1058. X
  1059. X    if (parseok)
  1060. X        if (! streq(tokenString, ")"))
  1061. X            compilError("array not terminated by right parenthesis",
  1062. X                tokenString);
  1063. X        else
  1064. X            ignore nextToken();
  1065. X    size = literalTop - base;
  1066. X    newLit = newArray(size);
  1067. X    for (i = size; i >= 1; i--) {
  1068. X        obj = literalArray[literalTop];
  1069. X        basicAtPut(newLit, i, obj);
  1070. X        decr(obj);
  1071. X        literalArray[literalTop] = nilobj;
  1072. X        literalTop = literalTop - 1;
  1073. X        }
  1074. X    return(genLiteral(newLit));
  1075. X}
  1076. X
  1077. Xstatic boolean term()
  1078. X{    boolean superTerm = false;    /* true if term is pseudo var super */
  1079. X
  1080. X    if (token == nameconst) {
  1081. X        superTerm = nameTerm(tokenString);
  1082. X        ignore nextToken();
  1083. X        }
  1084. X    else if (token == intconst) {
  1085. X        if ((tokenInteger >= 0) && (tokenInteger <= 2))
  1086. X            genInstruction(PushConstant, tokenInteger);
  1087. X        else
  1088. X            genInstruction(PushLiteral, 
  1089. X                genLiteral(newInteger(tokenInteger)));
  1090. X        ignore nextToken();
  1091. X        }
  1092. X    else if (token == floatconst) {
  1093. X        genInstruction(PushLiteral, genLiteral(newFloat(tokenFloat)));
  1094. X        ignore nextToken();
  1095. X        }
  1096. X    else if ((token == binary) && streq(tokenString, "-")) {
  1097. X        ignore nextToken();
  1098. X        if (token == intconst) {
  1099. X            if (tokenInteger == 1)
  1100. X                genInstruction(PushConstant, 3);
  1101. X            else
  1102. X                genInstruction(PushLiteral, 
  1103. X                    genLiteral(newInteger( - tokenInteger)));
  1104. X            }
  1105. X        else if (token == floatconst) {
  1106. X            genInstruction(PushLiteral,
  1107. X                genLiteral(newFloat(-tokenFloat)));
  1108. X            }
  1109. X        else
  1110. X            compilError("negation not followed",
  1111. X                "by number");
  1112. X        ignore nextToken();
  1113. X        }
  1114. X    else if (token == charconst) {
  1115. X        genInstruction(PushLiteral,
  1116. X            genLiteral(newChar(tokenInteger)));
  1117. X        ignore nextToken();
  1118. X        }
  1119. X    else if (token == symconst) {
  1120. X        genInstruction(PushLiteral,
  1121. X            genLiteral(newSymbol(tokenString)));
  1122. X        ignore nextToken();
  1123. X        }
  1124. X    else if (token == strconst) {
  1125. X        genInstruction(PushLiteral,
  1126. X            genLiteral(newStString(tokenString)));
  1127. X        ignore nextToken();
  1128. X        }
  1129. X    else if (token == arraybegin) {
  1130. X        genInstruction(PushLiteral, parseArray());
  1131. X        }
  1132. X    else if ((token == binary) && streq(tokenString, "(")) {
  1133. X        ignore nextToken();
  1134. X        expression();
  1135. X        if (parseok)
  1136. X            if ((token != closing) || ! streq(tokenString, ")"))
  1137. X                compilError("Missing Right Parenthesis","");
  1138. X            else
  1139. X                ignore nextToken();
  1140. X        }
  1141. X    else if ((token == binary) && streq(tokenString, "<"))
  1142. X        parsePrimitive();
  1143. X    else if ((token == binary) && streq(tokenString, "["))
  1144. X        block();
  1145. X    else
  1146. X        compilError("invalid expression start", tokenString);
  1147. X
  1148. X    return(superTerm);
  1149. X}
  1150. X
  1151. Xstatic parsePrimitive()
  1152. X{    int primitiveNumber, argumentCount;
  1153. X
  1154. X    if (nextToken() != intconst)
  1155. X        compilError("primitive number missing","");
  1156. X    primitiveNumber = tokenInteger;
  1157. X    ignore nextToken();
  1158. X    argumentCount = 0;
  1159. X    while (parseok && ! ((token == binary) && streq(tokenString, ">"))) {
  1160. X        ignore term();
  1161. X        argumentCount++;
  1162. X        }
  1163. X    genInstruction(DoPrimitive, argumentCount);
  1164. X    genCode(primitiveNumber);
  1165. X    ignore nextToken();
  1166. X}
  1167. X
  1168. Xstatic genMessage(toSuper, argumentCount, messagesym)
  1169. Xboolean toSuper;
  1170. Xint argumentCount;
  1171. Xobject messagesym;
  1172. X{
  1173. X    if (toSuper) {
  1174. X        genInstruction(DoSpecial, SendToSuper);
  1175. X        genCode(argumentCount);
  1176. X        }
  1177. X    else
  1178. X        genInstruction(SendMessage, argumentCount);
  1179. X    genCode(genLiteral(messagesym));
  1180. X}
  1181. X
  1182. Xstatic boolean unaryContinuation(superReceiver)
  1183. Xboolean superReceiver;
  1184. X{    int i;
  1185. X    boolean sent;
  1186. X    object messagesym;
  1187. X
  1188. X    while (parseok && (token == nameconst)) {
  1189. X        /* first check to see if it could be a temp by mistake */
  1190. X        for (i=1; i < temporaryTop; i++)
  1191. X            if (streq(tokenString, temporaryName[i]))
  1192. X                compilWarn("message same as temporary:",
  1193. X                    tokenString);
  1194. X        for (i=1; i < argumentTop; i++)
  1195. X            if (streq(tokenString, argumentName[i]))
  1196. X                compilWarn("message same as argument:",
  1197. X                    tokenString);
  1198. X        /* the next generates too many spurious messages */
  1199. X        /* for (i=1; i < instanceTop; i++)
  1200. X            if (streq(tokenString, instanceName[i]))
  1201. X                compilWarn("message same as instance",
  1202. X                    tokenString); */
  1203. X
  1204. X        sent = false;
  1205. X        messagesym = newSymbol(tokenString);
  1206. X        /* check for built in messages */
  1207. X        if (! superReceiver)
  1208. X            for (i = 0; (! sent) && unStrs[i] ; i++)
  1209. X                if (streq(tokenString, unStrs[i])) {
  1210. X                    genInstruction(SendUnary, i);
  1211. X                    sent = true;
  1212. X                    }
  1213. X        if (! sent) {
  1214. X            genMessage(superReceiver, 0, messagesym);
  1215. X            }
  1216. X        /* once a message is sent to super, reciever is not super */
  1217. X        superReceiver = false;
  1218. X        ignore nextToken();
  1219. X        }
  1220. X    return(superReceiver);
  1221. X}
  1222. X
  1223. Xstatic boolean binaryContinuation(superReceiver)
  1224. Xboolean superReceiver;
  1225. X{    int i;
  1226. X    boolean sent, superTerm;
  1227. X    object messagesym;
  1228. X
  1229. X    superReceiver = unaryContinuation(superReceiver);
  1230. X    while (parseok && (token == binary)) {
  1231. X        messagesym = newSymbol(tokenString);
  1232. X        ignore nextToken();
  1233. X        superTerm = term();
  1234. X        ignore unaryContinuation(superTerm);
  1235. X        sent = false;
  1236. X        /* check for built in messages */
  1237. X        if (! superReceiver) {
  1238. X            for (i = 0; (! sent) && binStrs[i]; i++)
  1239. X                if (messagesym == binSyms[i]) {
  1240. X                    genInstruction(SendBinary, i);
  1241. X                    sent = true;
  1242. X                    }
  1243. X
  1244. X            }
  1245. X        if (! sent) {
  1246. X            genMessage(superReceiver, 1, messagesym);
  1247. X            }
  1248. X        superReceiver = false;
  1249. X        }
  1250. X    return(superReceiver);
  1251. X}
  1252. X
  1253. Xstatic int optimizeBlock(instruction, dopop)
  1254. Xint instruction;
  1255. Xboolean dopop;
  1256. X{    int location;
  1257. X    boolean saveOB;
  1258. X
  1259. X    genInstruction(DoSpecial, instruction);
  1260. X    location = codeTop;
  1261. X    genCode(0);
  1262. X    if (dopop)
  1263. X        genInstruction(DoSpecial, PopTop);
  1264. X    ignore nextToken();
  1265. X    if (streq(tokenString, "[")) {
  1266. X        ignore nextToken();
  1267. X        saveOB = optimizedBlock;
  1268. X        optimizedBlock = true;
  1269. X        body();
  1270. X        optimizedBlock = saveOB;
  1271. X        if (! streq(tokenString, "]"))
  1272. X            compilError("missing close","after block");
  1273. X        ignore nextToken();
  1274. X        }
  1275. X    else {
  1276. X        ignore binaryContinuation(term());
  1277. X        genInstruction(SendUnary, 3 /* value command */);
  1278. X        }
  1279. X    codeArray[location] = codeTop;
  1280. X    return(location);
  1281. X}
  1282. X
  1283. Xstatic boolean keyContinuation(superReceiver)
  1284. Xboolean superReceiver;
  1285. X{    int i, j, argumentCount;
  1286. X    boolean sent, superTerm;
  1287. X    object messagesym;
  1288. X    char pattern[80];
  1289. X
  1290. X    superReceiver = binaryContinuation(superReceiver);
  1291. X    if (token == namecolon) {
  1292. X        if (streq(tokenString, "ifTrue:")) {
  1293. X            i = optimizeBlock(BranchIfFalse, false);
  1294. X            if (streq(tokenString, "ifFalse:")) {
  1295. X                codeArray[i] = codeTop + 3;
  1296. X                ignore optimizeBlock(Branch, true);
  1297. X                }
  1298. X            }
  1299. X        else if (streq(tokenString, "ifFalse:")) {
  1300. X            i = optimizeBlock(BranchIfTrue, false);
  1301. X            if (streq(tokenString, "ifTrue:")) {
  1302. X                codeArray[i] = codeTop + 3;
  1303. X                ignore optimizeBlock(Branch, true);
  1304. X                }
  1305. X            }
  1306. X        else if (streq(tokenString, "whileTrue:")) {
  1307. X            j = codeTop;
  1308. X            genInstruction(DoSpecial, Duplicate);
  1309. X            genInstruction(SendUnary, 3 /* value command */);
  1310. X            i = optimizeBlock(BranchIfFalse, false);
  1311. X            genInstruction(DoSpecial, PopTop);
  1312. X            genInstruction(DoSpecial, Branch);
  1313. X            genCode(j);
  1314. X            codeArray[i] = codeTop;
  1315. X            genInstruction(DoSpecial, PopTop);
  1316. X            }
  1317. X        else if (streq(tokenString, "and:"))
  1318. X            ignore optimizeBlock(AndBranch, false);
  1319. X        else if (streq(tokenString, "or:"))
  1320. X            ignore optimizeBlock(OrBranch, false);
  1321. X        else {
  1322. X            pattern[0] = '\0';
  1323. X            argumentCount = 0;
  1324. X            while (parseok && (token == namecolon)) {
  1325. X                ignore strcat(pattern, tokenString);
  1326. X                argumentCount++;
  1327. X                ignore nextToken();
  1328. X                superTerm = term();
  1329. X                ignore binaryContinuation(superTerm);
  1330. X                }
  1331. X            sent = false;
  1332. X
  1333. X            /* check for predefined messages */
  1334. X            messagesym = newSymbol(pattern);
  1335. X            if (! superReceiver) {
  1336. X                for (i = 0; (! sent) && binStrs[i]; i++)
  1337. X                    if (messagesym == binSyms[i]) {
  1338. X                        sent = true;
  1339. X                        genInstruction(SendBinary, i);
  1340. X                        }
  1341. X
  1342. X                for (i = 0; (! sent) && keyStrs[i]; i++)
  1343. X                    if (messagesym == keySyms[i]) {
  1344. X                        genInstruction(SendKeyword, i);
  1345. X                        sent = true;
  1346. X                        }
  1347. X                }
  1348. X
  1349. X            if (! sent) {
  1350. X                genMessage(superReceiver, argumentCount, messagesym);
  1351. X                }
  1352. X            }
  1353. X        superReceiver = false;
  1354. X        }
  1355. X    return(superReceiver);
  1356. X}
  1357. X
  1358. Xstatic continuation(superReceiver)
  1359. Xboolean superReceiver;
  1360. X{
  1361. X    superReceiver = keyContinuation(superReceiver);
  1362. X
  1363. X    while (parseok && (token == closing) && streq(tokenString, ";")) {
  1364. X        genInstruction(DoSpecial, Duplicate);
  1365. X        ignore nextToken();
  1366. X        ignore keyContinuation(superReceiver);
  1367. X        genInstruction(DoSpecial, PopTop);
  1368. X        }
  1369. X}
  1370. X
  1371. Xstatic expression()
  1372. X{    boolean superTerm;
  1373. X
  1374. X    superTerm = term();
  1375. X    if (parseok)
  1376. X        continuation(superTerm);
  1377. X}
  1378. X
  1379. Xstatic assignment(name)
  1380. Xchar *name;
  1381. X{    int i;
  1382. X    boolean done;
  1383. X
  1384. X    done = false;
  1385. X
  1386. X    /* it might be a temporary */
  1387. X    for (i = 1; (! done) && (i <= temporaryTop); i++)
  1388. X        if (streq(name, temporaryName[i])) {
  1389. X            genInstruction(PopTemporary, i-1);
  1390. X            done = true;
  1391. X            }
  1392. X
  1393. X    /* or it might be an instance variable */
  1394. X    for (i = 1; (! done) && (i <= instanceTop); i++)
  1395. X        if (streq(name, instanceName[i])) {
  1396. X            genInstruction(PopInstance, i-1);
  1397. X            done = true;
  1398. X            }
  1399. X
  1400. X    if (! done)
  1401. X        compilError("assignment to unknown name", name);
  1402. X}
  1403. X
  1404. Xstatic statement()
  1405. X{    char assignname[80];
  1406. X    boolean superReceiver = false;
  1407. X
  1408. X    if ((token == binary) && streq(tokenString, "^")) {
  1409. X        ignore nextToken();
  1410. X        expression();
  1411. X        if (inBlock)
  1412. X            genInstruction(DoSpecial, BlockReturn);
  1413. X        else
  1414. X            genInstruction(DoSpecial, StackReturn);
  1415. X        }
  1416. X    else if (token == nameconst) {    /* possible assignment */
  1417. X        ignore strcpy(assignname, tokenString);
  1418. X        ignore nextToken();
  1419. X        if ((token == binary) && streq(tokenString, "<-")) {
  1420. X            ignore nextToken();
  1421. X            expression();
  1422. X            if (inBlock || optimizedBlock)
  1423. X                if ((token == closing) && streq(tokenString,"]"))
  1424. X                    genInstruction(DoSpecial, Duplicate);
  1425. X            assignment(assignname);
  1426. X            if (inBlock && (token == closing) &&
  1427. X                streq(tokenString, "]"))
  1428. X                genInstruction(DoSpecial, StackReturn);
  1429. X            }
  1430. X        else {        /* not an assignment after all */
  1431. X            superReceiver = nameTerm(assignname);
  1432. X            continuation(superReceiver);
  1433. X            if ((token == closing) && streq(tokenString, "]")) {
  1434. X                if (inBlock && ! optimizedBlock)
  1435. X                    genInstruction(DoSpecial, StackReturn);
  1436. X                }
  1437. X            else
  1438. X                genInstruction(DoSpecial, PopTop);
  1439. X            }
  1440. X        }
  1441. X    else {
  1442. X        expression();
  1443. X        if ((token == closing) && streq(tokenString, "]")) {
  1444. X            if (inBlock && ! optimizedBlock)
  1445. X                genInstruction(DoSpecial, StackReturn);
  1446. X            }
  1447. X        else
  1448. X            genInstruction(DoSpecial, PopTop);
  1449. X        }
  1450. X}
  1451. X
  1452. Xstatic body()
  1453. X{
  1454. X    if (inBlock || optimizedBlock)
  1455. X        if ((token == closing) && streq(tokenString, "]")) {
  1456. X            genInstruction(PushConstant, 4);
  1457. X            if (! optimizedBlock)
  1458. X                genInstruction(DoSpecial, StackReturn);
  1459. X            return;
  1460. X            }
  1461. X
  1462. X    while(parseok) {
  1463. X        statement();
  1464. X        if (token == closing)
  1465. X            if (streq(tokenString,".")) {
  1466. X                ignore nextToken();
  1467. X                if (token == inputend)
  1468. X                    break;
  1469. X                }
  1470. X            else
  1471. X                break;
  1472. X        else
  1473. X            if (token == inputend)
  1474. X                break;
  1475. X        else {
  1476. X            compilError("invalid statement ending; token is ",
  1477. X                tokenString);
  1478. X            }
  1479. X        }
  1480. X}
  1481. X
  1482. Xstatic block()
  1483. X{    int saveTemporary, argumentCount, fixLocation;
  1484. X    boolean saveInBlock, saveOB;
  1485. X    object tempsym;
  1486. X
  1487. X    saveTemporary = temporaryTop;
  1488. X    argumentCount = 0;
  1489. X    ignore nextToken();
  1490. X    if ((token == binary) && streq(tokenString, ":")) {
  1491. X        while (parseok && (token == binary) && streq(tokenString,":")) {
  1492. X            if (nextToken() != nameconst)
  1493. X                compilError("name must follow colon",
  1494. X                    "in block argument list");
  1495. X                if (++temporaryTop > maxTemporary)
  1496. X                maxTemporary = temporaryTop;
  1497. X            argumentCount++;
  1498. X            if (temporaryTop > temporaryLimit)
  1499. X                compilError("too many temporaries in method","");
  1500. X            else {
  1501. X                tempsym = newSymbol(tokenString);
  1502. X                temporaryName[temporaryTop] = charPtr(tempsym);
  1503. X                }
  1504. X            ignore nextToken();
  1505. X            }
  1506. X        if ((token != binary) || ! streq(tokenString, "|"))
  1507. X            compilError("block argument list must be terminated",
  1508. X                    "by |");
  1509. X        ignore nextToken();
  1510. X        }
  1511. X    genInstruction(CreateBlock, argumentCount);
  1512. X    if (argumentCount != 0){
  1513. X        genCode(saveTemporary + 1);
  1514. X        }
  1515. X    fixLocation = codeTop;
  1516. X    genCode(0);
  1517. X    saveInBlock = inBlock;
  1518. X    saveOB = optimizedBlock;
  1519. X    inBlock = true;
  1520. X    optimizedBlock = false;
  1521. X    body();
  1522. X    if ((token == closing) && streq(tokenString, "]"))
  1523. X        ignore nextToken();
  1524. X    else
  1525. X        compilError("block not terminated by ]","");
  1526. X    codeArray[fixLocation] = codeTop;
  1527. X    inBlock = saveInBlock;
  1528. X    optimizedBlock = saveOB;
  1529. X    temporaryTop = saveTemporary;
  1530. X}
  1531. X
  1532. Xstatic temporaries()
  1533. X{    object tempsym;
  1534. X
  1535. X    temporaryTop = 0;
  1536. X    if ((token == binary) && streq(tokenString, "|")) {
  1537. X        ignore nextToken();
  1538. X        while (token == nameconst) {
  1539. X            if (++temporaryTop > maxTemporary)
  1540. X                maxTemporary = temporaryTop;
  1541. X            if (temporaryTop > temporaryLimit)
  1542. X                compilError("too many temporaries in method","");
  1543. X            else {
  1544. X                tempsym = newSymbol(tokenString);
  1545. X                temporaryName[temporaryTop] = charPtr(tempsym);
  1546. X                }
  1547. X            ignore nextToken();
  1548. X            }
  1549. X        if ((token != binary) || ! streq(tokenString, "|"))
  1550. X            compilError("temporary list not terminated by bar","");
  1551. X        else
  1552. X            ignore nextToken();
  1553. X        }
  1554. X}
  1555. X
  1556. Xstatic messagePattern()
  1557. X{    object argsym;
  1558. X
  1559. X    argumentTop = 0;
  1560. X    ignore strcpy(selector, tokenString);
  1561. X    if (token == nameconst)        /* unary message pattern */
  1562. X        ignore nextToken();
  1563. X    else if (token == binary) {    /* binary message pattern */
  1564. X        ignore nextToken();
  1565. X        if (token != nameconst) 
  1566. X            compilError("binary message pattern not followed by name",selector);
  1567. X        argsym = newSymbol(tokenString);
  1568. X        argumentName[++argumentTop] = charPtr(argsym);
  1569. X        ignore nextToken();
  1570. X        }
  1571. X    else if (token == namecolon) {    /* keyword message pattern */
  1572. X        selector[0] = '\0';
  1573. X        while (parseok && (token == namecolon)) {
  1574. X            ignore strcat(selector, tokenString);
  1575. X            ignore nextToken();
  1576. X            if (token != nameconst)
  1577. X                compilError("keyword message pattern",
  1578. X                    "not followed by a name");
  1579. X            if (++argumentTop > argumentLimit)
  1580. X                compilError("too many arguments in method","");
  1581. X            argsym = newSymbol(tokenString);
  1582. X            argumentName[argumentTop] = charPtr(argsym);
  1583. X            ignore nextToken();
  1584. X            }
  1585. X        }
  1586. X    else
  1587. X        compilError("illegal message selector", tokenString);
  1588. X}
  1589. X
  1590. Xboolean parse(method, text)
  1591. Xobject method;
  1592. Xchar *text;
  1593. X{    int i;
  1594. X    object bytecodes, theLiterals;
  1595. X    byte *bp;
  1596. X
  1597. X    lexinit(text);
  1598. X    parseok = true;
  1599. X    codeTop = 0;
  1600. X    literalTop = temporaryTop = argumentTop =0;
  1601. X    maxTemporary = 0;
  1602. X    inBlock = optimizedBlock = false;
  1603. X
  1604. X    messagePattern();
  1605. X    if (parseok)
  1606. X        temporaries();
  1607. X    if (parseok)
  1608. X        body();
  1609. X    if (parseok)
  1610. X        genInstruction(DoSpecial, SelfReturn);
  1611. X
  1612. X    if (! parseok)
  1613. X        basicAtPut(method, bytecodesInMethod, nilobj);
  1614. X    else {
  1615. X        bytecodes = newByteArray(codeTop);
  1616. X        bp = bytePtr(bytecodes);
  1617. X        for (i = 0; i < codeTop; i++) {
  1618. X            bp[i] = codeArray[i];
  1619. X            }
  1620. X        basicAtPut(method, messageInMethod, newSymbol(selector));
  1621. X        basicAtPut(method, bytecodesInMethod, bytecodes);
  1622. X        if (literalTop > 0) {
  1623. X            theLiterals = newArray(literalTop);
  1624. X            for (i = 1; i <= literalTop; i++) {
  1625. X                basicAtPut(theLiterals, i, literalArray[i]);
  1626. X                decr(literalArray[i]);
  1627. X                }
  1628. X            basicAtPut(method, literalsInMethod, theLiterals);
  1629. X            }
  1630. X        else
  1631. X            basicAtPut(method, literalsInMethod, nilobj);
  1632. X        basicAtPut(method, stackSizeInMethod, newInteger(6));
  1633. X        basicAtPut(method, temporarySizeInMethod,
  1634. X            newInteger(1 + maxTemporary));
  1635. X        basicAtPut(method, textInMethod, newStString(text));
  1636. X        return(true);
  1637. X        }
  1638. X    return(false);
  1639. X}
  1640. /
  1641. echo 'x - queen.st'
  1642. sed 's/^X//' > queen.st << '/'
  1643. XClass Queen Object #row #column #neighbor
  1644. XMethod Queen
  1645. X    setColumn: aNumber neighbor: aQueen
  1646. X        column <- aNumber.
  1647. X        neighbor <- aQueen
  1648. X
  1649. X|
  1650. XMethod Queen
  1651. X    checkRow: testRow column: testColumn | columnDifference |
  1652. X        columnDifference <- testColumn - column.
  1653. X        (((row = testRow) or: 
  1654. X            [ row + columnDifference = testRow]) or:
  1655. X            [ row - columnDifference = testRow])
  1656. X                ifTrue: [ ^ true ].
  1657. X        (neighbor notNil)
  1658. X            ifTrue: [ ^ neighbor checkRow: testRow 
  1659. X                    column: testColumn ]
  1660. X            ifFalse: [ ^ false ]
  1661. X
  1662. X|
  1663. XMethod Queen
  1664. X    first
  1665. X        (neighbor notNil)
  1666. X            ifTrue: [ neighbor first ].
  1667. X        row <- 1.
  1668. X        ^ self testPosition
  1669. X
  1670. X|
  1671. XMethod Queen
  1672. X    next
  1673. X        (row = 8)
  1674. X            ifTrue: [ ((neighbor isNil) or: [neighbor next isNil])
  1675. X                ifTrue: [ ^ nil ].
  1676. X                row <- 0 ].
  1677. X        row <- row + 1.
  1678. X        ^ self testPosition
  1679. X
  1680. X|
  1681. XMethod Queen
  1682. X    testPosition
  1683. X        (neighbor isNil) ifTrue: [ ^ self ].
  1684. X        (neighbor checkRow: row column: column)
  1685. X            ifTrue: [ ^ self next ]
  1686. X            ifFalse: [ ^ self ]
  1687. X
  1688. X|
  1689. XMethod Queen
  1690. X    result
  1691. X        ^ ((neighbor isNil)
  1692. X            ifTrue: [ List new ]
  1693. X            ifFalse: [ neighbor result ] )
  1694. X                addLast: row
  1695. X
  1696. X|
  1697. XMethod Test
  1698. X    queen        | lastQueen |
  1699. X        lastQueen <- nil.
  1700. X        (1 to: 8) do: [:i | lastQueen <- Queen new;
  1701. X                    setColumn: i neighbor: lastQueen ].
  1702. X        lastQueen first.
  1703. X        (lastQueen result asArray = #(1 5 8 6 3 7 2 4) )
  1704. X            ifTrue: ['8 queens test passed' print]
  1705. X            ifFalse: [smalltalk error: '8queen test failed']
  1706. X
  1707. X|
  1708. /
  1709. echo 'Part 02 of small.v2 complete.'
  1710. exit
  1711.