home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume11 / little-st / part01 next >
Text File  |  1987-10-03  |  60KB  |  1,927 lines

  1. Subject:  v11i086:  Little Smalltalk interpreter, Part01/03
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rs@uunet.UU.NET
  5.  
  6. Submitted-by: Tim Budd <budd@cs.orst.edu>
  7. Posting-number: Volume 11, Issue 86
  8. Archive-name: little-st/part01
  9.  
  10. The following is version two of the Little Smalltalk system, distributed
  11. in three parts.  Little Smalltalk is an interpreter for the language
  12. Smalltalk.
  13.  
  14. Questions or comments should be sent to Tim Budd,
  15.     budd@oregon-state.csnet
  16.     budd@cs.orst.edu    (128.193.32.1)
  17.     {tektronix, hp-pcd}!orstcs!budd
  18.  
  19. -----------cut here--------------------------------------------
  20. : To unbundle, sh this file
  21. echo unbundling READ_ME 1>&2
  22. cat >READ_ME <<'End'
  23. .\" information on Little Smalltalk, version 2, beta release
  24. .SH
  25. General Overview
  26. .PP
  27. First, the obvious facts.  This is not Smalltalk-80, nor even Smalltalk-V.
  28. This is the second version of the Little Smalltalk system, the first version
  29. of which is described in the book recently published by Addison-Wesley*.
  30. .FS
  31. * \fIA Little Smalltalk\fP, by Timothy A. Budd.  Published by Addison
  32. Wesley, 1987.  In better bookshops everywhere.
  33. .FE
  34. Version two is smaller and faster; does more in Smalltalk, not in C; and is
  35. designed to be more portable to a wider variety of machines (we are working
  36. on versions now for various PCs).  
  37. .PP
  38. My attitude towards the language has been
  39. rather cavalier; what I liked I kept and what I didn't like I tossed out.
  40. This is explained in more detail in my book and in the end of this note.
  41. As a consequence, individuals familiar with ST-80 or ST-V will be struck 
  42. by how much they are missing, and I make no apologies for this.  On the
  43. other hand, you don't find ST-V posted to net.sources.  Among the features
  44. you won't find here are metaclasses, class methods, windows, graphics 
  45. support, and more.
  46. .PP
  47. What you will find is a small language that does give you the flavor of
  48. object oriented programming at very little cost.  We are working to improve
  49. the system, and hope to distribute new versions as we develop them, 
  50. as well as porting it to a wide range of machines.
  51. If you find (and preferably, fix!) bugs let us know.
  52. If you make nice additions let us know.
  53. If you want to make complements let us know.
  54. If you want to make complaints let us know.
  55. If you want support you just might be out of luck.
  56. .PP
  57. This software is entirely public domain.  You are encouraged to give it
  58. to as many friends as you may have.  As a courtesy, I would appreciate it
  59. if you left my name on the code as the author, but I make no other claims
  60. to it (I also, of course, disavow any liability for any bizarre things you
  61. may choose to do with it).  Enjoy.
  62. .SH
  63. Building the System
  64. .PP
  65. There are three steps involving in building the system; making the parser
  66. (the component used to generate the initial object image), making the
  67. bytecode interpreter, and making the object image.
  68. .PP
  69. After you have unbundled all the files, to create the parser type
  70. .DS I
  71. make parse
  72. .DE
  73. .PP
  74. The resulting program, called parse, is used to generate the object image
  75. initially loaded into the bytecode interpreter.
  76. .PP
  77. Next, make the interpreter itself by typing
  78. .DS I
  79. make st
  80. .DE
  81. .PP
  82. Note that the interpreter and the parser share some files.
  83. .PP
  84. Finally, produce an initial object image.  The image created when you type
  85. .DS I
  86. make sunix
  87. .DE
  88. .LP
  89. is the smallest and fastest.  It is a single process version of smalltalk.
  90. A buggy multiprocess version can be created by typing ``make munix''*.
  91. .FS
  92. * Multi processing from munix is done entirely in Smalltalk.
  93. While this is a good idea from the point of view of keeping the bytecode
  94. interpreter small and giving one the greatest flexibility, there seems to
  95. be a dramatic performance penalty.  I'm considering the alternatives.
  96. .FE
  97. Of more interest, an image containing test cases (***currently only
  98. the 8 queens***) can be generated by typing ``make stest''.
  99. In the latter case, the command ``test all'', when given in response to the
  100. prompt (see below), runs all the test cases.
  101. .PP
  102. Once you have created an object image, type 
  103. .DS I
  104. st
  105. .DE
  106. .LP
  107. to run the system.
  108. By default the image file ``imageFile'' is read.  You can optionally
  109. use a different image file by giving the name on the command line following
  110. the st command.
  111. .SH
  112. Getting Started
  113. .PP
  114. When you start version two Little Smalltalk under Unix, you will be given a 
  115. prompt.
  116. You can enter expressions in response to the prompt, and the system will 
  117. evaluate them (although it will not print the result unless you request it).
  118. For example:
  119. .DS I
  120. >    (4 + 5) print
  121. 7
  122. .DE
  123. .PP
  124. You can create a new global variable (a variable known every place, including
  125. the command line) by simply inserting a command into the dictionary that
  126. maintains the names of all global variables.  You use as key the name of
  127. the new global variable (as a Symbol), and as value the initial value to 
  128. be associated with the variable.
  129. .DS I
  130. >    globalNames at: #i put: 17
  131. >    i print
  132. 17
  133. .DE
  134. .PP
  135. Global variables cannot be modified by the assignment arrow.  In particular,
  136. the following gives an error:
  137. .DS I
  138. >    i <- 16
  139. Compiler error: unknown variable i
  140. .DE
  141. .PP
  142. Global variables can, however, be used in expressions:
  143. .DS I
  144. >    (i + 3) print
  145. 20
  146. .DE
  147. .PP
  148. The most common use for global variables is creating a new Class.  A Class
  149. is simply a global variable, by convention (but only convention) being given
  150. a name beginning with an uppercase letter.  For example:
  151. .DS I
  152. >    globalNames at: #Employee put: Class new
  153. .DE
  154. .PP
  155. This creates a new class called \fBEmployee\fP, an instance of 
  156. class \fBClass\fP.  Various messages, understood by instances of class
  157. \fBClass\fP, can be used to initialize various features of this new object.
  158. (This would be a good time to take a peek at the file ``basicclasses'', which
  159. contains a textual description of all the methods used in the standard
  160. classes.  Note carefully the methods used in class Class).
  161. .DS I
  162. >    globalNames superClass: Object
  163. >    globalNames name: #Employee
  164. >    globalNames variables: #(department salary)
  165. .DE
  166. .PP
  167. The most important initializing message is \fBaddMethod\fP, which 
  168. drops you into an editor (currently only \fIvi\fP), in which you enter
  169. the body of a method.  When you exit the editor the method is compiled,
  170. and either entered into the method dictionary for the class (if there
  171. are no errors) or a sequence of error messages are displayed on the output
  172. device.
  173. .PP
  174. To save an object image, type the command
  175. .DS I
  176. smalltalk saveImage
  177. .DE
  178. You will be prompted for the name of the image file.
  179. .SH
  180. Changes from Little Smalltalk version one
  181. .PP
  182. The following changes have been made from version one to version two:
  183. .IP \(bu
  184. The user interface is slightly different.  This is most apparent in the way
  185. new classes are added (see above).
  186. .IP \(bu
  187. Much (very much) more of the system is now written in Smalltalk, rather
  188. than C.  This allows the user to see, and modify it if they wish.
  189. This also means that the virtual machine is now much smaller.
  190. .IP \(bu
  191. The pseudo variable selfProcess is no longer supported.
  192. The variables true, false and nil are now treated as global variables, not
  193. pseudo variables (see below).
  194. There are plans for adding processes to version two, but they have not
  195. been formalized yet.
  196. .IP \(bu
  197. Global variables are now supported; in fact classes are now simply global
  198. variables, as are the variables true, false, smalltalk and nil.
  199. The global variable globalNames contains the dictionary of all currently
  200. known global variables and their values.
  201. (Pool variables are still not supported).
  202. .IP \(bu
  203. The internal bytecodes are slightly different.  In particular, the bytecode
  204. representing ``send to super'' has been eliminated, and a bytecode representing
  205. ``do a primitive'' has been added.
  206. .IP \(bu
  207. The Collection hierarchy has been rearranged.  The rational for this change
  208. is explained in more detail in another essay.
  209. (possibly not written yet).
  210. .IP \(bu
  211. Some methods, most notably the error message methods, have been moved out
  212. of class Object and into class Smalltalk.
  213. .IP \(bu
  214. The syntax for primitives is different; the keyword \fBprimitive\fP has been
  215. eliminated, and named primitives are now gone as well.
  216. Fewer actions are performed by primitives, having been
  217. replaced by Smalltalk methods.
  218. .IP \(bu
  219. Command line options, such as the fast load feature, have been eliminated.
  220. However, since version two reads in a binary object image, not a textual
  221. file, loading should be considerably faster.
  222. .SH
  223. Electronic Communication
  224. .PP
  225. Here is my address, various net addresses:
  226. .DS I
  227. Tim Budd
  228. Oregon State University
  229. Department of Computer Science
  230. Corvallis, Oregon 97331 USA
  231. (503) 754-3273
  232.  
  233. budd@oregon-state.csnet
  234.  
  235. {tektronix, hp-pcd} !orstcs!budd
  236. .DE
  237. .SH
  238. Changes
  239. .PP
  240. I want to emphasize that this is not even a beta-test version (does that
  241. make it an alpha or a gamma version?).  I will be making a number of
  242. changes, hopefully just additions to the initial image, in the next
  243. few months.  In addition, I hope to prepare versions for other machines,
  244. notably the Macintosh and the IBM PC.  I am also encouraging others to
  245. port the system to new machines.  If you have done so, please let me
  246. know.
  247. End
  248. echo unbundling Bugs 1>&2
  249. cat >Bugs <<'End'
  250. objects are limited to size 256 
  251.     this mostly limits the text (char) size of methods - to 512 chars.
  252.     this could be fixed by changing memory.c.
  253.  
  254. nested array literals don't seem to work properly
  255.  
  256. radices other than 10 aren't implemented.
  257.  
  258. parser should leave method text in method, so it can be edited dynamically
  259. (does this now, but it should be an option).
  260.  
  261. The collection hierarchy has been completely reorginized (this isn't a bug)
  262.     many of the more obscure messages are left unimplmented.
  263.     many of the abstract classes are eliminated
  264.     Bags have been eliminated (they can be replaced by lists)
  265.     collections are now magnitudes (set subset relations)
  266.  
  267. The basic classes are somewhat incomplete, in particular
  268.     points aren't implemented
  269.     radians are implemented (neither are trig functions)
  270.  
  271. Bytearrays are a bit odd.  In particular,
  272.     converting to bytearrays gives something too big (by twice)
  273.     converting bytearrays to strings can cause bugs if the last
  274.     byte is not zero (causing non null terminated strings)
  275.  
  276. Files aren't implemented; 
  277.     when they are addMethod and editMethod should be changed to use
  278.     Smalltalk files.
  279.  
  280. Semaphores and processes aren't implemented yet - even in the multiprocess
  281.     version
  282.     initial experiments aren't encouraging - 
  283.     they seem to be too slow.
  284.     
  285. PROJECTS______________________________________________________________
  286. For those with time on their hands and nothing to do, here is a list
  287. of several projects that need doing.
  288.  
  289. 1. Profiling indicates that about 45% of execution time is spent in the
  290. routine ``execute'', in interp.c.  Rewrite this in your favorite assembly
  291. language to speed it up.
  292.  
  293. 2. Rewrite the memory manager.  Possible changes
  294.     a. use garbage collection of some sort
  295.     b. allow big objects (bigger than 256 words)
  296.  
  297. 3. Rewrite the process manager in assembly language, permitting the
  298.     Smalltalk process stack to exist intermixed with the C
  299.     execution stack.
  300.  
  301. 4. Port to your favorite machine, making the interface fit the machine.
  302. End
  303. echo unbundling Makefile 1>&2
  304. cat >Makefile <<'End'
  305. #
  306. # Makefile for Little Smalltalk, version 2
  307. #
  308. CFLAGS = -p -O
  309.  
  310. COMMONc = memory.c names.c lex.c parser.c
  311. COMMONo = memory.o names.o lex.o parser.o
  312. PARSEc  = comp.c $(COMMONc) image.c
  313. PARSEo  = comp.o $(COMMONo) image.o
  314. STc     = main.c $(COMMONc) process.c primitive.c interp.c
  315. STo     = main.o $(COMMONo) process.o primitive.o interp.o
  316. classes = basicclasses unixclasses multclasses unix2classes testclasses
  317. B1F     = READ_ME Bugs Makefile at top *.h comp.c image.c main.c process.c
  318. B2F     = $(COMMONc) primitive.c interp.c
  319. B3F    = $(classes) stest.out
  320.  
  321. install: parse sunix st
  322.     echo "created single process version, see docs for more info"
  323.  
  324. #
  325. # parse - the object image parser.  
  326. # used to build the initial object image
  327. #
  328. parse: $(PARSEo)
  329.     cc -o parse $(CFLAGS) $(PARSEo)
  330.  
  331. parseprint:
  332.     pr *.h $(PARSEc) | lpr
  333.  
  334. parselint:
  335.     lint $(PARSEc)
  336.  
  337. #
  338. # st - the actual bytecode interpreter
  339. # runs bytecodes from the initial image, or another image
  340. #
  341. st: $(STo)
  342.     cc $(CFLAGS) -o st $(STo) -lm
  343.  
  344. stlint: 
  345.     lint $(STc)
  346.  
  347. stprint:
  348.     pr *.h $(STc) | lpr
  349.  
  350. report: memory.o report.o
  351.     cc -o report memory.o report.o
  352.  
  353. #
  354. # image - build the initial object image
  355. #
  356. classlpr:
  357.     pr $(classes) | lpr
  358.  
  359. sunix: parse 
  360.     parse basicclasses unixclasses
  361.  
  362. munix: parse
  363.     parse basicclasses multclasses unix2classes
  364.  
  365. stest: parse
  366.     parse basicclasses unixclasses testclasses
  367.  
  368. mtest: parse
  369.     parse basicclasses multclasses unix2classes testclasses
  370.  
  371. #
  372. # distribution bundles
  373. #
  374.  
  375. bundles:
  376.     bundle $(B1F) >bundle.1
  377.     bundle $(B2F) >bundle.2
  378.     bundle $(B3F) >bundle.3
  379.  
  380. tar:
  381.     tar cvf ../smalltalk.v2.tar .
  382.     compress -c ../smalltalk.v2.tar >../smalltalk.v2.tar.Z
  383. End
  384. echo unbundling at 1>&2
  385. cat >at <<'End'
  386. .LP
  387. (note: this is the first of a series of essays descriging how various 
  388. features of the Little Smalltalk bytecodes work).
  389. .SH
  390. Where It's At
  391. .PP
  392. This short note explains how the messages \fBat:\fP, \fBat:put:\fP, and their 
  393. relatives are defined and used in collections.  We start by discussing the 
  394. simplest form of collections, arrays and strings.
  395. .PP
  396. The message \fBat:\fP is not defined anywhere in class \fBArray\fP or any of
  397. its subclasses.  Instead, this message is inherited from 
  398. class \fBCollection\fP, which defines it using the following method:
  399. .DS I
  400. \fBat:\fP index
  401.     \(ua self at: index
  402.         ifAbsent: [ smalltalk error: 'index to at: illegal' ]
  403. .DE
  404. .PP
  405. The functioning of the message \fBerror:\fP is the topic of another essay;
  406. it is sufficient for our purposes to note only that this message prints out
  407. the error string and returns nil.  By redefining \fBat:\fP in this fashion,
  408. the subclasses of \fBCollection\fP need not be concerned about how to deal
  409. with errors in cases where no error recovery action has been specified.
  410. .PP
  411. For an array, an index is out of bounds if it is either less than 1 or greater
  412. than the size of the array.  This is tested by a method in class \fBArray\fP:
  413. .DS I
  414. \fBincludesKey:\fP index
  415.     ^ index between: 1 and: self size
  416. .DE
  417. .PP
  418. The message \fBsize\fP is defined in class \fBArray\fP in terms of the
  419. message \fBbasicSize\fP
  420. .DS I
  421. \fBsize\fP
  422.     ^ self basicSize
  423. .DE
  424. .PP
  425. The message \fBbasicSize\fP (as well as \fBbasicAt:\fP, discussed below) 
  426. is inherited from class 
  427. \fBObject\fP.  It can be used on any object; on non-arrays it returns
  428. the number of instance variables for the object.  The messages \fBbasicSize\fP 
  429. and \fBbasicAt:put:\fP can be used by system
  430. classes, for example debuggers, to access instance variables in an object 
  431. without having explicit access to the instance variables.  One must be 
  432. careful, however,
  433. \fBbasicAt:\fP produces a system error, and not a Smalltalk error message,
  434. if it is given an index value that is out of range.
  435. .PP
  436. Using \fBincludesKey:\fP for a test, a value is only accessed if the index
  437. is legal.  The following method appears in class \fBArray\fP:
  438. .DS I
  439. \fBat:\fP index \fBifAbsent:\fP exceptionBlock
  440.     ^ (self includesKey: index)
  441.         ifTrue: [ self basicAt: index ]
  442.         ifFalse: [ exceptionBlock value ]
  443. .DE
  444. .PP
  445. A subclass of \fBArray\fP is the class \fBByteArray\fP.  A byte array is a form
  446. of array in which the elements can only take on values from zero to 255, or
  447. to put it another way, values that can be stored in one byte.
  448. On most 16 bit machines, we can store two such bytes in the space it takes
  449. to store one object pointer.  Thus, the message \fBsize\fP is redefined
  450. in class \fBByteArray\fP as follows:
  451. .DS I
  452. \fBsize\fP
  453.     \(ua self basicSize * 2
  454. .DE
  455. .LP
  456. Note that this implies that byte arrays always have an even number of
  457. elements.  Next the message \fBbasicAt:\fP is redefined to use a byte,
  458. instead of object, form of index.  This is accomplished using a primitive
  459. method, (the message \fBbasicAt:\fP is handled in a similar fashion in
  460. class \fBObject\fP, only using a different primitive).
  461. .DS I
  462. \fBbasicAt:\fP index
  463.     \(ua <26 self index>
  464. .DE
  465. .PP
  466. Like a byte array, a string can also store two byte values in the space
  467. it takes to store a single object pointer.  Unlike a byte array, however,
  468. a string can be any length, not just an even length.  Therefore the message
  469. \fBsize\fP is redefned in class \fBString\fP, a subclass of \fBByteArray\fP.
  470. .DS I
  471. \fBsize\fP
  472.     \(ua <14 self>
  473. .DE
  474. .PP
  475. Another difference between a string and a byte array is that the value
  476. returned by a string must be a character, not an integer.  Therefore
  477. \fBbasicAt:\fP must also be redefined.  By using the message \fBbasicAt:\fP
  478. defined in \fBByteArray\fP, (the superclass of String, and therefore accessible
  479. via the pseudo variable \fBsuper\fP) the method can obtain the integer value 
  480. of the appropriate character.  This value is then used to create a new
  481. instance of class \fBChar\fP:
  482. .DS I
  483. \fBbasicAt:\fP index
  484.     \(ua Char new; value: (super basicAt: index)
  485. .DE
  486. .PP
  487. A value is placed into an array using the message \fPat:put:\fP.  As with 
  488. \fBat:\fP, a value should only be placed if the index represents a legal
  489. subscript.  This is checked in the following method:
  490. .DS I
  491. \fBat:\fP index \fBput:\fP value
  492.     (self includesKey: index)
  493.         ifTrue: [ self basicAt: index put: value ]
  494.         ifFalse: [ smalltalk error: 
  495.             'illegal index to at:put: for array' ]
  496. .DE
  497. .PP
  498. As was the case with \fBbasicAt:\fP, one version of \fBbasicAt:put:\fP,
  499. to be used by arrays of objects, is defined as part of class \fBObject\fP.
  500. A different version is found in class \fBByteArray\fP.  Finally a third 
  501. version, which first checks to see if the argument is a Character, is found
  502. in class \fBString\fP.
  503. .DS I
  504. \fBat:\fP index \fBput:\fP aValue
  505.     (aValue isMemberOf: Char)
  506.         ifTrue: [ super basicAt: index put: aValue asciiValue ]
  507.         ifFalse: [ smalltalk error:
  508.             'cannot put non Char into string' ]
  509. .DE
  510. .SH
  511. Exercises
  512. .IP 1.
  513. Describe the sequence of messages used to respond to the following:
  514. .DS B
  515. x \(<- #(1 2 3) at: 2
  516. .DE
  517. .IP 2.
  518. Describe how the execution of the above expression could be speeded up by
  519. adding new methods.  Note if your methods are specific to arrays of objects,
  520. arrays of bytes, or strings.
  521. End
  522. echo unbundling top 1>&2
  523. cat >top <<'End'
  524. .SH
  525. Who's On Top?
  526. .PP
  527. One of the most important decisions to be made in designing a new user
  528. interface (or front end) for the Little Smalltalk system is whether the user
  529. interface management code should sit on top of the Smalltalk bytecode 
  530. interpreter, setting up commands and invoking the interpreter to execute them,
  531. or underneith the bytecode interpreter, being invoked by Smalltalk, via the
  532. mechanism of primitive methods.  Both schemes have advantages and disadvantages
  533. which we will discuss in this essay.
  534. .PP
  535. In a simple interface, placing Smalltalk on top is often easier.  The main
  536. driver need only set up one initial call to the Smalltalk bytecode interpreter,
  537. and thereafter everything is done in Smalltalk.  For example, we might put
  538. initialization code in a method in class \fBSmalltalk\fP, as follows:
  539. .DS L
  540. Class Smalltalk
  541.     getString
  542.         \(ua <1>
  543. |
  544.     run        | string |
  545.         [ '>    ' printNoReturn.
  546.            string <- smalltalk getString. 
  547.            string notNil ]
  548.             whileTrue: [ (string size > 0)
  549.                     ifTrue: [ smalltalk doIt: string ] ]
  550. ]
  551. .DE
  552. .PP
  553. Once the bytecode interpreter is started on the method \fBrun\fP, it will
  554. loop continuously, reading commands from the user (via the method 
  555. \fBgetString\fP) and executing them (via the method \fBdoIt:\fP).
  556. Presumably the user has some way of indicating end of input, such as the
  557. unix control-D convention, which causes \fBgetString\fP to return the
  558. value nil.  The \fIif\fP statement inside the while loop
  559. insures that if the user simply hits the return key execution will quickly 
  560. loop back to the prompt.
  561. .PP
  562. Besides making the initialization for the Little Smalltalk system easy,
  563. this approach also has the advantage of putting more code into Smalltalk
  564. itself, where the user can see it and (presumably) modify it if they wish.
  565. A general guideline is that it is better to put as much into Smalltalk
  566. as possible, since Smalltalk is easier to write and the bytecode representation
  567. usually smaller than the equivalent code in C.
  568. Never the less, there are valid reasons why an implementor might choose
  569. a different technique.
  570. .PP
  571. For example, if there are many other activities which should command the 
  572. attention of the controlling program (window updates, mouse motions) the 
  573. Smalltalk code may not be able to respond fast enough, or might become too 
  574. large and complex to be workable.
  575. In this case the only alternative is to have the front end respond directly
  576. to events, and only invoke the Smalltalk interpreter as time permits.
  577. In basic terms, the front end would perform the loop written in the method
  578. \fBinit\fP shown above (along with handling various other tasks), and then 
  579. call upon the method in class \fBSmalltalk\fP
  580. to execute the message \fBdoIt:\fP.
  581. .SH
  582. How to Do It
  583. .PP
  584. In either of the two schemes described above, an important message is 
  585. \fBdoIt:\fP, which takes a string (presumably representing a Smalltalk
  586. expression) and performs it.  An easy way to perform this message is to
  587. make a method out of the expression, by appending a message pattern
  588. on front, and then pass the string to the method parser.  If the method
  589. parser is successful, the method can then be executed.
  590. .DS L
  591. doIt: aString        | method |
  592.     method <- Method new.
  593.     method text: ( 'proceed ', aString ).
  594.     (method compileWithClass: Smalltalk)
  595.         ifTrue: [ method executeWith: #( 0 ) ]
  596. .DE
  597. .PP
  598. The message \fBcompileWithClass:\fP compiles the method as if it was
  599. appearing as part of class Smalltalk.  If compilation is successful,
  600. the message \fBexecuteWith:\fP executes the message, using as arguments
  601. the array #(0).  The array that accompanies this message must have at
  602. least one element, as the first value is used as the receiver for
  603. the method.
  604. Similar techniques can be used for the message \fBprintIt:\fP, if desired.
  605. .SH
  606. The Other End
  607. .PP
  608. The opposite extreme from the front end are those messages that originate
  609. within the bytecode interpreter and must be communicated to the user.
  610. We can divide these values into four categories:
  611. .IP 1.
  612. System errors.  These are all funnelled through the routine sysError(), found
  613. in memory.c.  System errors are caused by dramatically wrong conditions,
  614. and should generally cause the system to abort after printing the message
  615. passed as argument to sysError().
  616. .IP 2.
  617. Compiler errors.  As we noted above, the method compiler is used to
  618. parse expressions typed directly at the keyboard, so these message can
  619. also arise in that manner.  These are all funnelled through the routine
  620. compilError(), found in parse.c.  These should print their arguments 
  621. (two strings), in an appropriate location on the users screen.
  622. Execution continues normally after call.
  623. .IP 3.
  624. Various primitives, found in primitive.c, are also used to print strings
  625. on the users terminal.  In particular, an appropriate meaning should be
  626. given to the message \fBprint\fP in class \fBString\fP.  What appropriate
  627. means is undoubtedly implementation specific.
  628. .IP 4.
  629. Finally, and perhaps most importantly, there must be some means provided
  630. to allow users to enter and edit methods.  The interface for this task
  631. is standard; instances of class \fBClass\fP must respond to the messages
  632. \fBaddMethod\fP and \fBeditMethod:\fP, the latter taking as argument a
  633. symbol representing the name of a method.  How they achieve their two
  634. tasks is, however, implementation specific.
  635. Under Unix, a simple implementation adds a new primitive for Strings;
  636. this primitive copies the string into a temporary file, starts up the
  637. editor on the file, and returns the contents of the file when the user
  638. exits the editor.  Having this capability, the method editing code
  639. can be given as follows.  In class \fBClass\fP:
  640. .DS L
  641.     addMethod
  642.         self doEdit: ''
  643. |
  644.     editMethod: name        | theMethod |
  645.         theMethod <- methods at: name
  646.                 ifAbsent: [ 'no such method ' print. \(ua nil ].
  647.         self doEdit: theMethod text
  648. |
  649.     doEdit: startingText        | theMethod |
  650.         theMethod <- Method new;
  651.             text: startingText edit.
  652.         (theMethod compileWithClass: self)
  653.             ifTrue: [ methods at: theMethod name put: theMethod ]
  654. .DE
  655. .LP
  656. And in class \fBString\fP:
  657. .DS L
  658.     edit
  659.         \(ua <19 self>
  660. .DE
  661. .LP
  662. Here primitive 19 performs all the tasks of creating the temporary file,
  663. starting the editor, and creating the string representing the file
  664. contents when the editor is exited.
  665. .PP
  666. Alternative techniques, for example using windowing, would undoubtedly
  667. be more complicated.
  668. End
  669. echo unbundling env.h 1>&2
  670. cat >env.h <<'End'
  671. /*
  672.     Little Smalltalk, version two
  673.     Written by Tim Budd, Oregon State University, July 1987
  674.  
  675.     environmental factors
  676.  
  677.     This include file gathers together environmental factors that
  678.     are likely to change from one C compiler to another, or from
  679.     one system to another.  These include:
  680.  
  681.     1. The type boolean.  A typedef is used to define this;
  682.         on some older systems typedefs may not work, and a
  683.         # define statement should be used instead.
  684.         The only other typedef appears in memory.h
  685.  
  686.     2. The statement ignore - this appears on functions being used
  687.         as procedures.  It has no effect except as a lint
  688.         silencer, and is also the only place where the type
  689.         ``void'' appears.  If your system doesn't support void,
  690.         define ignore to be nothing.
  691.  
  692.     3. The datatype byte - an 8-bit unsigned quantity.
  693.         On some systems the datatype ``unsigned char'' does not
  694.         work - for these some experimentation may be necessary.
  695.         The macro byteToInt() converts a byte value into an integer.
  696.         Again a typedef is used, which can be replaced by a
  697.         define.
  698.  
  699.     4. If your system does not have enumerated constants, the define
  700.         NOENUM should be given, in which case enumerated constants
  701.         are replaced by defines.
  702.  
  703.     5. The define constant INITIALIMAGE should give the name (as a path)
  704.         of the default standard object image file.
  705. */
  706.  
  707. # define true 1
  708. # define false 0
  709.  
  710. typedef int boolean;
  711.  
  712. # define ignore (void)
  713.  
  714. typedef unsigned char byte;
  715.  
  716. # define byteToInt(b) (b)
  717.  
  718. # define INITIALIMAGE "imageFile"
  719.  
  720. # define TEMPFILENAME "/usr/tmp/lstXXXXXX"
  721. End
  722. echo unbundling interp.h 1>&2
  723. cat >interp.h <<'End'
  724. /*
  725.     Little Smalltalk, version 2
  726.     Written by Tim Budd, Oregon State University, July 1987
  727. */
  728. /*
  729.     symbolic definitions for the bytecodes
  730. */
  731.  
  732. # define Extended 0
  733. # define PushInstance 1
  734. # define PushArgument 2
  735. # define PushTemporary 3
  736. # define PushLiteral 4
  737. # define PushConstant 5
  738. # define PushGlobal 6
  739. # define PopInstance 7
  740. # define PopTemporary 8
  741. # define SendMessage 9
  742. # define SendUnary 10
  743. # define SendBinary 11
  744. # define SendKeyword 12
  745. # define DoPrimitive 13
  746. # define CreateBlock 14
  747. # define DoSpecial 15
  748.  
  749. /* types of special instructions (opcode 15) */
  750.  
  751. # define SelfReturn 1
  752. # define StackReturn 2
  753. # define BlockReturn 3
  754. # define Duplicate 4
  755. # define PopTop 5
  756. # define Branch 6
  757. # define BranchIfTrue 7
  758. # define BranchIfFalse 8
  759. # define AndBranch 9
  760. # define OrBranch 10
  761. # define SendToSuper 11
  762. End
  763. echo unbundling lex.h 1>&2
  764. cat >lex.h <<'End'
  765. /*
  766.     Little Smalltalk, version 2
  767.     Written by Tim Budd, Oregon State University, July 1987
  768. */
  769. /*
  770.     values returned by the lexical analyzer
  771. */
  772.  
  773. # ifndef NOENUMS
  774.  
  775. typedef enum tokensyms { nothing, name, namecolon, 
  776.     intconst, floatconst, charconst, symconst,
  777.     arraybegin, strconst, binary, closing, inputend} tokentype;
  778. # endif
  779.  
  780. # ifdef NOENUMS
  781. # define tokentype int
  782. # define nothing 0
  783. # define name 1
  784. # define namecolon 2
  785. # define intconst 3
  786. # define floatconst 4
  787. # define charconst 5
  788. # define symconst 6
  789. # define arraybegin 7
  790. # define strconst 8
  791. # define binary 9
  792. # define closing 10
  793. # define inputend 11
  794.  
  795. # endif
  796.  
  797. extern tokentype nextToken();
  798.  
  799. extern tokentype token;        /* token variety */
  800. extern char tokenString[];    /* text of current token */
  801. extern int tokenInteger;    /* integer (or character) value of token */
  802. extern double tokenFloat;    /* floating point value of token */
  803. End
  804. echo unbundling memory.h 1>&2
  805. cat >memory.h <<'End'
  806. /*
  807.     Little Smalltalk, version 2
  808.     Written by Tim Budd, Oregon State University, July 1987
  809. */
  810.  
  811. # define streq(a,b) (strcmp(a,b) == 0)
  812.  
  813. /*
  814.     The first major decision to be made in the memory manager is what
  815. an entity of type object really is.  Two obvious choices are a pointer (to 
  816. the actual object memory) or an index into an object table.  We decided to
  817. use the latter, although either would work.
  818.     Similarly, one can either define the token object using a typedef,
  819. or using a define statement.  Either one will work (check this?)
  820. */
  821.  
  822. typedef short object;
  823.  
  824. /*
  825.     The memory module itself is defined by over a dozen routines.
  826. All of these could be defined by procedures, and indeed this was originally
  827. done.  However, for efficiency reasons, many of these procedures can be
  828. replaced by macros generating in-line code.  For the latter approach
  829. to work, the structure of the object table must be known.  For this reason,
  830. it is given here.  Note, however, that ONLY the macros described in this
  831. file make use of this structure: therefore modifications or even complete
  832. replacement is possible as long as the interface remains consistent
  833.  
  834. */
  835.  
  836. struct objectStruct {
  837.     object class;
  838.     short referenceCount;
  839.     byte size;
  840.     byte type;
  841.     object *memory;
  842.     };
  843.  
  844. extern struct objectStruct objectTable[];
  845.  
  846. /* types of object memory */
  847. # define objectMemory 0
  848. # define byteMemory 1
  849. # define charMemory 2
  850. # define floatMemory 3
  851.  
  852. # define isString(x) ((objectTable[x>>1].type == charMemory) || (objectTable[x>>1].type == byteMemory))
  853. # define isFloat(x)  (objectTable[x>>1].type == floatMemory)
  854.  
  855. /*
  856.     The most basic routines to the memory manager are incr and decr,
  857. which increment and decrement reference counts in objects.  By separating
  858. decrement from memory freeing, we could replace these as procedure calls
  859. by using the following macros:*/
  860. extern object incrobj;
  861. # define incr(x) if ((incrobj=(x))&&!isInteger(incrobj)) \
  862. objectTable[incrobj>>1].referenceCount++
  863. #  define decr(x) if (((incrobj=x)&&!isInteger(incrobj))&&\
  864. (--objectTable[incrobj>>1].referenceCount<=0)) sysDecr(incrobj);
  865. /*
  866. notice that the argument x is first assigned to a global variable; this is
  867. in case evaluation of x results in side effects (such as assignment) which
  868. should not be repeated. */
  869.  
  870. # ifndef incr
  871. extern void incr();
  872. # endif
  873. # ifndef decr
  874. extern void decr();
  875. # endif
  876.  
  877. /*
  878.     The next most basic routines in the memory module are those that
  879. allocate blocks of storage.  There are three routines:
  880.     allocObject(size) - allocate an array of objects
  881.     allocByte(size) - allocate an array of bytes
  882.     allocChar(size) - allocate an array of character values
  883.     allocSymbol(value) - allocate a string value
  884.     allocInt(value) - allocate an integer value
  885.     allocFloat(value) - allocate a floating point object
  886. again, these may be macros, or they may be actual procedure calls
  887. */
  888.  
  889. extern object alcObject();    /* the actual routine */
  890. # define allocObject(size) alcObject(size, objectMemory)
  891. # define allocByte(size) alcObject(size, byteMemory)
  892. # define allocChar(size) alcObject(size, charMemory)
  893. extern object allocSymbol();
  894. # define allocInt(value) ((value<0)?value:(value<<1)+1)
  895. extern object allocFloat();
  896.  
  897. /*
  898.     integer objects are (but need not be) treated specially.
  899. In this memory manager, negative integers are just left as is, but
  900. position integers are changed to x*2+1.  Either a negative or an odd
  901. number is therefore an integer, while a nonzero even number is an
  902. object pointer (multiplied by two).  Zero is reserved for the object ``nil''
  903. Since newInteger does not fill in the class field, it can be given here.
  904. If it was required to use the class field, it would have to be deferred
  905. until names.h
  906. */
  907.  
  908. extern object intobj;
  909. # define isInteger(x) ((x) & 0x8001)
  910. # define newInteger(x) ( (intobj = x)<0 ? intobj : (intobj<<1)+1 )
  911. # define intValue(x) ( (intobj = x)<0 ? intobj : (intobj>>1) )
  912.  
  913. /*
  914.     in addition to alloc floating point routine given above,
  915. another routine must be provided to go the other way.  Note that
  916. the routine newFloat, which fills in the class field as well, must
  917. wait until the global name table is known, in names.h
  918. */
  919. extern double floatValue();
  920.  
  921. /*
  922.     there are four routines used to access fields within an object.
  923. Again, some of these could be replaced by macros, for efficiency
  924.     basicAt(x, i) - ith field (start at 1) of object x
  925.     basicAtPut(x, i, v) - put value v in object x
  926.     byteAt(x, i) - ith field (start at 0) of object x
  927.     byteAtPut(x, i, v) - put value v in object x
  928. */
  929.  
  930. # define basicAt(x,i) (sysMemPtr(x)[i-1])
  931.  
  932. # ifndef basicAt
  933. extern object basicAt();
  934. # endif
  935. # ifndef basicAtPut
  936. extern void basicAtPut();
  937. # endif
  938. # ifndef byteAt
  939. extern int byteAt();
  940. # endif
  941. # ifndef byteAtPut
  942. extern void byteAtPut();
  943. # endif
  944.  
  945. /*
  946.     Finally, a few routines (or macros) are used to access or set
  947. class fields and size fields of objects
  948. */
  949.  
  950. # define classField(x) objectTable[x>>1].class
  951. # define setClass(x,y) incr(classField(x)=y)
  952.  
  953. # define objectSize(x) byteToInt(objectTable[x>>1].size)
  954.  
  955. # define sysMemPtr(x) objectTable[x>>1].memory
  956. extern object sysobj;
  957. # define memoryPtr(x) (isInteger(sysobj = x)?(object *) 0:sysMemPtr(sysobj))
  958. # define bytePtr(x) ((byte *) memoryPtr(x))
  959. # define charPtr(x) ((char *) memoryPtr(x))
  960.  
  961. # define nilobj (object) 0
  962.  
  963. /*
  964.     these two objects are the source of all objects in the system
  965. */
  966. extern object symbols;
  967. extern object globalNames;
  968. End
  969. echo unbundling names.h 1>&2
  970. cat >names.h <<'End'
  971. /*
  972.     Little Smalltalk, version 2
  973.     Written by Tim Budd, Oregon State University, July 1987
  974. */
  975. /*
  976.     names and sizes of internally object used internally in the system
  977. */
  978.  
  979. # define classSize 6
  980. # define nameInClass 1
  981. # define sizeInClass 2
  982. # define methodsInClass 3
  983. # define superClassInClass 4
  984. # define variablesInClass 5
  985.  
  986. # define methodSize 6
  987. # define textInMethod 1
  988. # define messageInMethod 2
  989. # define bytecodesInMethod 3
  990. # define literalsInMethod 4
  991. # define stackSizeInMethod 5
  992. # define temporarySizeInMethod 6
  993.  
  994. # define contextSize 6
  995. # define methodInContext 1
  996. # define methodClassInContext 2
  997. # define argumentsInContext 3
  998. # define temporariesInContext 4
  999.  
  1000. # define blockSize 6
  1001. # define contextInBlock 1
  1002. # define argumentCountInBlock 2
  1003. # define argumentLocationInBlock 3
  1004. # define bytecountPositionInBlock 4
  1005. # define creatingInterpreterInBlock 5
  1006.  
  1007. # define InterpreterSize 6
  1008. # define contextInInterpreter 1
  1009. # define previousInterpreterInInterpreter 2
  1010. # define creatingInterpreterInInterpreter 3
  1011. # define stackInInterpreter 4
  1012. # define stackTopInInterpreter 5
  1013. # define byteCodePointerInInterpreter 6
  1014.  
  1015. extern object nameTableLookup();
  1016.  
  1017. # define globalSymbol(s) nameTableLookup(globalNames, newSymbol(s))
  1018.  
  1019. extern object trueobj;        /* the pseudo variable true */
  1020. extern object falseobj;        /* the pseudo variable false */
  1021. extern object smallobj;        /* the pseudo variable smalltalk */
  1022. extern object blockclass;    /* the class ``Block'' */
  1023. extern object contextclass;    /* the class ``Context'' */
  1024. extern object intclass;        /* the class ``Integer'' */
  1025. extern object intrclass;    /* the class ``Interpreter'' */
  1026. extern object symbolclass;    /* the class ``Symbol'' */
  1027. extern object stringclass;    /* the class ``String'' */
  1028.  
  1029. extern object newSymbol();    /* new smalltalk symbol */
  1030. extern object newArray();    /* new array */
  1031. extern object newStString();    /* new smalltalk string */
  1032. extern object newFloat();    /* new floating point number */
  1033. End
  1034. echo unbundling process.h 1>&2
  1035. cat >process.h <<'End'
  1036. /*
  1037.     Little Smalltalk, version 2
  1038.     Written by Tim Budd, Oregon State University, July 1987
  1039. */
  1040. /*
  1041.     constants and types used by process manager. 
  1042.     See process.c and interp.c for more details.
  1043. */
  1044. /*
  1045.     if there are no enumerated types, make tasks simply integer constants
  1046. */
  1047. # ifdef NOENUMS
  1048. # define taskType int
  1049.  
  1050. # define sendMessageTask 1
  1051. # define sendSuperTask   2
  1052. # define ReturnTask     3
  1053. # define BlockReturnTask 4
  1054. # define BlockCreateTask 5
  1055. # define ContextExecuteTask 6
  1056.  
  1057. #endif
  1058.  
  1059. # ifndef NOENUMS
  1060.  
  1061. typedef enum {sendMessageTask, sendSuperTask, ReturnTask, BlockReturnTask,
  1062.         BlockCreateTask, ContextExecuteTask} taskType;
  1063.  
  1064. # endif
  1065.  
  1066. extern int finalStackTop;    /* stack top when finished with interpreter */
  1067. extern int finalByteCounter;    /* bytecode counter when finished with interpreter */
  1068. extern int argumentsOnStack;    /* position of arguments on stack for mess send */
  1069. extern object messageToSend;    /* message to send */
  1070. extern object returnedObject;    /* object returned from message */
  1071. extern taskType  finalTask;        /* next task to do (see below) */
  1072.  
  1073.  
  1074. End
  1075. echo unbundling comp.c 1>&2
  1076. cat >comp.c <<'End'
  1077. /*
  1078.     Little Smalltalk, version 2
  1079.     Written by Tim Budd, Oregon State University, July 1987
  1080.  
  1081.     Unix specific front end for the initial object image maker
  1082. */
  1083.  
  1084. # include <stdio.h>
  1085. # include "env.h"
  1086. # include "memory.h"
  1087. # include "names.h"
  1088.  
  1089. main(argc, argv) 
  1090. int argc;
  1091. char **argv;
  1092. { FILE *fp;
  1093.     int i;
  1094.  
  1095.     initMemoryManager();
  1096.  
  1097.     buildInitialNameTables();
  1098.  
  1099.     if (argc == 1)
  1100.         readFile(stdin);
  1101.     else
  1102.         for (i = 1; i < argc; i++) {
  1103.             fp = fopen(argv[i], "r");
  1104.             if (fp == NULL)
  1105.                 sysError("can't open file", argv[i]);
  1106.             else {
  1107.                 readFile(fp);
  1108.                 ignore fclose(fp);
  1109.                 }
  1110.             }
  1111.  
  1112.     fp = fopen("imageFile", "w");
  1113.     if (fp == NULL) sysError("error during image file open","imageFile");
  1114.     imageWrite(fp);
  1115.     ignore fclose(fp);
  1116.  
  1117. }
  1118. End
  1119. echo unbundling image.c 1>&2
  1120. cat >image.c <<'End'
  1121. /*
  1122.     Little Smalltalk, version 2
  1123.     Written by Tim Budd, Oregon State University, July 1987
  1124.  
  1125.     routines used in the making of the initial object image
  1126. */
  1127.  
  1128. # include <stdio.h>
  1129. # include "env.h"
  1130. # include "memory.h"
  1131. # include "names.h"
  1132. # include "lex.h"
  1133.  
  1134. # define SymbolTableSize 71
  1135. # define GlobalNameTableSize 53
  1136. # define MethodTableSize 39
  1137.  
  1138. # define globalNameSet(sym, value) nameTableInsert(globalNames, sym, value)
  1139. /*
  1140.     the following classes are used repeately, so we put them in globals.
  1141. */
  1142. static object ObjectClass;
  1143. static object ClassClass;
  1144. static object LinkClass;
  1145. static object DictionaryClass;
  1146. static object ArrayClass;
  1147.  
  1148. /*
  1149.     we read the input a line at a time, putting lines into the following
  1150.     buffer.  In addition, all methods must also fit into this buffer.
  1151. */
  1152. # define TextBufferSize 1024
  1153. static char textBuffer[TextBufferSize];
  1154.  
  1155. /*
  1156.     nameTableInsert is used to insert a symbol into a name table.
  1157.     see names.c for futher information on name tables
  1158. */
  1159. nameTableInsert(table, symbol, value)
  1160. object table, symbol, value;
  1161. {    object link, newLink, nextLink, entry;
  1162.     int hash;
  1163.  
  1164.     if (objectSize(table) < 3)
  1165.         sysError("attempt to insert into","too small name table");
  1166.     else {
  1167.         hash = 3 * ( symbol % (objectSize(table) / 3));
  1168.         entry = basicAt(table, hash+1);
  1169.         if ((entry == nilobj) || (entry == symbol)) {
  1170.             basicAtPut(table, hash+1, symbol);
  1171.             basicAtPut(table, hash+2, value);
  1172.             }
  1173.         else {
  1174.             newLink = allocObject(3);
  1175.             incr(newLink);
  1176.             setClass(newLink, globalSymbol("Link"));
  1177.             basicAtPut(newLink, 1, symbol);
  1178.             basicAtPut(newLink, 2, value);
  1179.             link = basicAt(table, hash+3);
  1180.             if (link == nilobj)
  1181.                 basicAtPut(table, hash+3, newLink);
  1182.             else
  1183.                 while(1)
  1184.                     if (basicAt(link,1) == symbol) {
  1185.                         basicAtPut(link, 2, value);
  1186.                         break;
  1187.                         }
  1188.                     else if ((nextLink = basicAt(link, 3)) == nilobj) {
  1189.                         basicAtPut(link, 3, newLink);
  1190.                         break;
  1191.                         }
  1192.                     else
  1193.                         link = nextLink;
  1194.             decr(newLink);
  1195.             }
  1196.     }
  1197. }
  1198.  
  1199. /*
  1200.     there is sort of a chicken and egg problem about building the 
  1201.     first classes.
  1202.     in order to do it, you need symbols, 
  1203.     but in order to make symbols, you need the class Symbol.
  1204.     the routines makeClass and buildInitialNameTable attempt to get 
  1205.     carefully get around this initialization problem
  1206. */
  1207.  
  1208. static object makeClass(name)
  1209. char *name;
  1210. {    object theClass, theSymbol;
  1211.  
  1212.     /* this can only be called once newSymbol works properly */
  1213.  
  1214.     theClass = allocObject(classSize);
  1215.     theSymbol = newSymbol(name);
  1216.     basicAtPut(theClass, nameInClass, theSymbol);
  1217.     globalNameSet(theSymbol, theClass);
  1218.     setClass(theClass, ClassClass);
  1219.  
  1220.     return(theClass);
  1221. }
  1222.  
  1223. buildInitialNameTables()
  1224. {    object symbolString, classString;
  1225.     int hash;
  1226.     char *p;
  1227.  
  1228.     /* build the table that contains all symbols */
  1229.     symbols = allocObject(2 * SymbolTableSize);
  1230.     incr(symbols);
  1231.     /* build the table that contains all global names */
  1232.     globalNames = allocObject(3 * GlobalNameTableSize);
  1233.     incr(globalNames);
  1234.  
  1235.     /* next create class Symbol, so we can call newSymbol */
  1236.     /* notice newSymbol uses the global variable symbolclass */
  1237.     symbolString = allocSymbol("Symbol");
  1238.     symbolclass =  allocObject(classSize);
  1239.     setClass(symbolString, symbolclass);
  1240.     basicAtPut(symbolclass, nameInClass, symbolString);
  1241.     /* we recreate the hash computation used by newSymbol */
  1242.     hash = 0;
  1243.     for (p = "Symbol"; *p; p++)
  1244.         hash += *p;
  1245.     if (hash < 0) hash = - hash;
  1246.     hash %= (objectSize(symbols) / 2);
  1247.     basicAtPut(symbols, 2*hash + 1, symbolString);
  1248.     globalNameSet(symbolString, symbolclass);
  1249.     /* now the routine newSymbol should work properly */
  1250.  
  1251.     /* now go on to make class Class so we can call makeClass */
  1252.     ClassClass = allocObject(classSize);
  1253.     classString = newSymbol("Class");
  1254.     basicAtPut(ClassClass, nameInClass, classString);
  1255.     globalNameSet(classString, ClassClass);
  1256.     setClass(ClassClass, ClassClass);
  1257.     setClass(symbolclass, ClassClass);
  1258.  
  1259.     /* now create a few other important classes */
  1260.     ObjectClass = makeClass("Object");
  1261.     LinkClass = makeClass("Link");
  1262.     setClass(nilobj, makeClass("UndefinedObject"));
  1263.     DictionaryClass = makeClass("Dictionary");
  1264.     ArrayClass = makeClass("Array");
  1265.     setClass(symbols, DictionaryClass);
  1266.     setClass(globalNames, DictionaryClass);
  1267.     
  1268.     globalNameSet(newSymbol("globalNames"), globalNames);
  1269. }
  1270.  
  1271. /*
  1272.     findClass gets a class object,
  1273.     either by finding it already or making it
  1274.     in addition, it makes sure it has a size, by setting
  1275.     the size to zero if it is nil.
  1276. */
  1277. static object findClass(name)
  1278. char *name;
  1279. {    object newobj;
  1280.  
  1281.     newobj = globalSymbol(name);
  1282.     if (newobj == nilobj)
  1283.         newobj = makeClass(name);
  1284.     if (basicAt(newobj, sizeInClass) == nilobj)
  1285.         basicAtPut(newobj, sizeInClass, newInteger(0));
  1286.     return(newobj);
  1287. }
  1288.  
  1289. /*
  1290.     readDeclaration reads a declaration of a class
  1291. */
  1292. static readDeclaration()
  1293. {    object classObj, super, vars;
  1294.     int i, size, instanceTop;
  1295.     object instanceVariables[15];
  1296.  
  1297.     if (nextToken() != name)
  1298.         sysError("bad file format","no name in declaration");
  1299.     classObj = findClass(tokenString);
  1300.     size = 0;
  1301.     if (nextToken() == name) {    /* read superclass name */
  1302.         super = findClass(tokenString);
  1303.         basicAtPut(classObj, superClassInClass, super);
  1304.         size = intValue(basicAt(super, sizeInClass));
  1305.         ignore nextToken();
  1306.         }
  1307.     if (token == name) {        /* read instance var names */
  1308.         instanceTop = 0;
  1309.         while (token == name) {
  1310.             instanceVariables[instanceTop++] = newSymbol(tokenString);
  1311.             size++;
  1312.             ignore nextToken();
  1313.             }
  1314.         vars = newArray(instanceTop);
  1315.         for (i = 0; i < instanceTop; i++)
  1316.             basicAtPut(vars, i+1, instanceVariables[i]);
  1317.         basicAtPut(classObj, variablesInClass, vars);
  1318.         }
  1319.     basicAtPut(classObj, sizeInClass, newInteger(size));
  1320. }
  1321.  
  1322. /*
  1323.     readInstance - read an instance directive 
  1324. */
  1325. static readInstance()
  1326. {    object classObj, newObj;
  1327.     int size;
  1328.  
  1329.     if (nextToken() != name)
  1330.         sysError("no name","following instance command");
  1331.     classObj = globalSymbol(tokenString);
  1332.     if (nextToken() != name)
  1333.         sysError("no instance name","in instance command");
  1334.  
  1335.     /* now make a new instance of the class -
  1336.         note that we can't do any initialization */
  1337.     size = intValue(basicAt(classObj, sizeInClass));
  1338.     newObj = allocObject(size);
  1339.     setClass(newObj, classObj);
  1340.     globalNameSet(newSymbol(tokenString), newObj);
  1341. }
  1342.  
  1343. /*
  1344.     readClass reads a class method description
  1345. */
  1346. static readClass(fd)
  1347. FILE *fd;
  1348. {    object classObj, methTable, theMethod, selector;
  1349.     char *eoftest, lineBuffer[80];
  1350.  
  1351.     /* if we haven't done it already, read symbols now */
  1352.     if (trueobj == nilobj)
  1353.         initCommonSymbols();
  1354.  
  1355.     if (nextToken() != name)
  1356.         sysError("missing name","following Class keyword");
  1357.     classObj = findClass(tokenString);
  1358.     setInstanceVariables(classObj);
  1359. fprintf(stderr,"class %s\n", charPtr(basicAt(classObj, nameInClass)));
  1360.  
  1361.     /* find or create a methods table */
  1362.     methTable = basicAt(classObj, methodsInClass);
  1363.     if (methTable == nilobj) {
  1364.         methTable = allocObject(MethodTableSize);
  1365.         setClass(methTable, globalSymbol("Dictionary"));
  1366.         basicAtPut(classObj, methodsInClass, methTable);
  1367.         }
  1368.  
  1369.     /* now go read the methods */
  1370.     do {
  1371.         textBuffer[0] = '\0';
  1372.         while((eoftest = fgets(lineBuffer, 80, fd)) != NULL) {
  1373.             if ((lineBuffer[0] == '|') || (lineBuffer[0] == ']'))
  1374.                 break;
  1375.             ignore strcat(textBuffer, lineBuffer);
  1376.             }
  1377.         if (eoftest == NULL) {
  1378.             sysError("unexpected end of file","while reading method");
  1379.             break;
  1380.             }
  1381.         /* now we have a method */
  1382.         theMethod = allocObject(methodSize);
  1383.         setClass(theMethod, globalSymbol("Method"));
  1384.         if (parse(theMethod, textBuffer)) {
  1385.             selector = basicAt(theMethod, messageInMethod);
  1386. fprintf(stderr,"method %s\n", charPtr(selector));
  1387.             nameTableInsert(methTable, selector, theMethod);
  1388.             }
  1389.         else {
  1390.             /* get rid of unwanted method */
  1391.             incr(theMethod);
  1392.             decr(theMethod);
  1393. fprintf(stderr,"push return to continue\n");
  1394. gets(textBuffer);
  1395.             }
  1396.         
  1397.     } while (lineBuffer[0] != ']');
  1398. }
  1399.  
  1400. /*
  1401.     readFile reads a class descriptions file
  1402. */
  1403. readFile(fd)
  1404. FILE *fd;
  1405. {
  1406.     while(fgets(textBuffer, TextBufferSize, fd) != NULL) {
  1407.         lexinit(textBuffer);
  1408.         if (token == inputend)
  1409.             ; /* do nothing, get next line */
  1410.         else if ((token == binary) && streq(tokenString, "*"))
  1411.             ; /* do nothing, its a comment */
  1412.         else if ((token == name) && streq(tokenString, "Declare"))
  1413.             readDeclaration();
  1414.         else if ((token == name) && streq(tokenString,"Instance"))
  1415.             readInstance();
  1416.         else if ((token == name) && streq(tokenString,"Class"))
  1417.             readClass(fd);
  1418.         else 
  1419.             fprintf("unknown line %s\n", textBuffer);
  1420.         }
  1421. }
  1422. End
  1423. echo unbundling main.c 1>&2
  1424. cat >main.c <<'End'
  1425. /*
  1426.     Little Smalltalk, version 2
  1427.     Written by Tim Budd, Oregon State University, July 1987
  1428.  
  1429.     driver (front-end) for bytecode interpreter.
  1430. */
  1431. # include <stdio.h>
  1432. # include "env.h"
  1433. # include "memory.h"
  1434. # include "names.h"
  1435. # include "interp.h"
  1436.  
  1437. extern int processStackTop;
  1438. extern object processStack[];
  1439. extern char tempfilename[];
  1440.  
  1441.  
  1442. main(argc, argv) 
  1443. int argc;
  1444. char **argv;
  1445. {
  1446. FILE *fp;
  1447.  
  1448. initMemoryManager();
  1449.  
  1450. if ((argc == 1) || ((argc > 1) && streq(argv[1],"-"))){
  1451.     fp = fopen(INITIALIMAGE,"r");
  1452.     if (fp == NULL)
  1453.         sysError("cannot read image file",INITIALIMAGE);
  1454.     }
  1455. else {
  1456.     fp = fopen(argv[1], "r");
  1457.     if (fp == NULL)
  1458.         sysError("cannot read image file", argv[1]);
  1459.     }
  1460. imageRead(fp);
  1461. ignore fclose(fp);
  1462.  
  1463. initCommonSymbols();
  1464.  
  1465. ignore strcpy(tempfilename, TEMPFILENAME);
  1466. ignore mktemp(tempfilename);
  1467.  
  1468. fprintf(stderr,"initially %d objects\n", objcount());
  1469.  
  1470. /* now we are ready to start */
  1471. prpush(smallobj);
  1472. sendMessage(newSymbol("init"), getClass(smallobj), 0);
  1473. flushstack();
  1474.  
  1475. fprintf(stderr,"finally %d objects\n", objcount());
  1476.  
  1477. if (argc > 2) {
  1478.     fp = fopen(argv[2],"w");
  1479.     if (fp == NULL)
  1480.         sysError("cannot write image file",argv[2]);
  1481.     fprintf(stderr,"creating image file %s\n", argv[2]);
  1482.     imageWrite(fp);
  1483.     ignore fclose(fp);
  1484.     }
  1485. }
  1486. End
  1487. echo unbundling process.c 1>&2
  1488. cat >process.c <<'End'
  1489. /*
  1490.     Little Smalltalk, version 2
  1491.     Written by Tim Budd, Oregon State University, July 1987
  1492.  
  1493.     Process Manager
  1494.  
  1495.     This module manages the stack of pending processes.
  1496.     SendMessage is called when it is desired to send a message to an
  1497.     object.  It looks up the method associated with the class of
  1498.     the receiver, then executes it.
  1499.     A block context is created only when it is necessary, and when it
  1500.     is required the routine executeFromContext is called instead of
  1501.     sendMessage.
  1502.     DoInterp is called by a primitive method to execute an interpreter,
  1503.     it returns the interpreter to which execution should continue
  1504.     following execution.
  1505. */
  1506. # include <stdio.h>
  1507. # include "env.h"
  1508. # include "memory.h"
  1509. # include "names.h"
  1510. # include "process.h"
  1511.  
  1512. # define ProcessStackMax 2000
  1513.  
  1514.     /* values set by interpreter when exiting */
  1515. int finalStackTop;    /* stack top when finished with interpreter */
  1516. int finalByteCounter;    /* bytecode counter when finished with interpreter */
  1517. int argumentsOnStack;    /* position of arguments on stack for mess send */
  1518. object messageToSend;    /* message to send */
  1519. object returnedObject;    /* object returned from message */
  1520. taskType finalTask;    /* next task to do (see below) */
  1521.  
  1522. static object blockReturnContext;
  1523.  
  1524. object processStack[ProcessStackMax];
  1525. int processStackTop = 0;
  1526.  
  1527. /*
  1528.     we cache recently used methods, in case we want them again
  1529. */
  1530.  
  1531. # define ProcessCacheSize 101    /* a suitable prime number */
  1532.  
  1533. struct {
  1534.     object startClass, messageSymbol, methodClass, theMethod;
  1535.     } methodCache[ProcessCacheSize];
  1536.  
  1537. prpush(newobj)
  1538. object newobj;
  1539. {
  1540.     incr(processStack[processStackTop++] = newobj);
  1541.     if (processStackTop >= ProcessStackMax)
  1542.         sysError("stack overflow","process stack");
  1543. }
  1544.  
  1545. /* flush out cache so new methods can be read in */
  1546. flushMessageCache()
  1547. {    int i;
  1548.  
  1549.     for (i = 0; i < ProcessCacheSize; i++)
  1550.         methodCache[i].messageSymbol = nilobj;
  1551. }
  1552.  
  1553. static object findMethod(hash, message, startingClass)
  1554. int hash;
  1555. object message, startingClass;
  1556. {    object method, class, methodtable;
  1557.  
  1558.     /* first examine cache */
  1559.     if ((methodCache[hash].messageSymbol == message) &&
  1560.         (methodCache[hash].startClass == startingClass)) {
  1561.         /* found it in cache */
  1562.         method = methodCache[hash].theMethod;
  1563.         }
  1564.     else {    /* must look in methods tables */
  1565.         method = nilobj;
  1566.         class = startingClass;
  1567.         while ( class != nilobj ) {
  1568.             methodtable = basicAt(class, methodsInClass);
  1569.             if (methodtable != nilobj)
  1570.                 method = nameTableLookup(methodtable, message);
  1571.             if (method != nilobj) {
  1572.                 /* fill in cache */
  1573.                 methodCache[hash].messageSymbol = message;
  1574.                 methodCache[hash].startClass = startingClass;
  1575.                 methodCache[hash].methodClass = class;
  1576.                 methodCache[hash].theMethod = method;
  1577.                 class = nilobj;
  1578.                 }
  1579.             else
  1580.                 class = basicAt(class, superClassInClass);
  1581.             }
  1582.         }
  1583.  
  1584.     return(method);
  1585. }
  1586.  
  1587. /* newContext - create a new context.  Note this returns three values,
  1588. via side effects
  1589. */
  1590. static newContext(method, methodClass, contextobj, argobj, tempobj)
  1591. object method, methodClass, *contextobj, argobj, *tempobj;
  1592. {    int temporarysize;
  1593.  
  1594.     *contextobj = allocObject(contextSize);
  1595.     incr(*contextobj);
  1596.     setClass(*contextobj, contextclass);
  1597.     basicAtPut(*contextobj, methodInContext, method);
  1598.     basicAtPut(*contextobj, methodClassInContext, methodClass);
  1599.     basicAtPut(*contextobj, argumentsInContext, argobj);
  1600.     temporarysize = intValue(basicAt(method, temporarySizeInMethod));
  1601.     *tempobj = newArray(temporarysize);
  1602.     basicAtPut(*contextobj, temporariesInContext, *tempobj);
  1603. }
  1604.  
  1605. sendMessage(message, startingClass, argumentPosition)
  1606. object message, startingClass;
  1607. int argumentPosition;
  1608. {    object method, methodClass, size;
  1609.     object contextobj, tempobj, argobj, errMessage;
  1610.     int i, hash, bytecounter, temporaryPosition, errloc;
  1611.     int argumentsize, temporarySize;
  1612.     boolean done;
  1613.  
  1614.     /* compute size of arguments part of stack */
  1615.     argumentsize = processStackTop - argumentPosition;
  1616.  
  1617.     hash = (message + startingClass) % ProcessCacheSize;
  1618.     method = findMethod(hash, message, startingClass);
  1619. /*fprintf(stderr,"sending message %s class %s\n", charPtr(message), charPtr(basicAt(startingClass, nameInClass)));*/
  1620.  
  1621.     if (method == nilobj) {        /* didn't find it */
  1622.         errMessage = newSymbol("class:doesNotRespond:");
  1623.         if (message == errMessage)
  1624.             /* better give up */
  1625.             sysError("didn't find method", charPtr(message));
  1626.         else {
  1627.             errloc = processStackTop;
  1628.             prpush(smallobj);
  1629.             prpush(startingClass);
  1630.             prpush(message);
  1631.             sendMessage(errMessage, getClass(smallobj), errloc);
  1632.             }
  1633.         }
  1634.     else {            /* found it, start execution */
  1635.         /* initialize things for execution */
  1636.         bytecounter = 0;
  1637.         done = false;
  1638.  
  1639.         /* allocate temporaries */
  1640.         temporaryPosition = processStackTop;
  1641.         size = basicAt(method, temporarySizeInMethod);
  1642.         if (! isInteger(size))
  1643.             sysError("temp size not integer","in method");
  1644.         else
  1645.             for (i = temporarySize = intValue(size); i > 0; i--)
  1646.                 prpush(nilobj);
  1647.         methodClass = methodCache[hash].methodClass;
  1648.  
  1649.         while( ! done ) {
  1650.             execute(method, bytecounter, 
  1651.                 processStack, processStackTop,
  1652.                 &processStack[argumentPosition],
  1653.                 &processStack[temporaryPosition]);
  1654.             bytecounter = finalByteCounter;
  1655.             processStackTop = finalStackTop;
  1656.  
  1657.             switch(finalTask) {
  1658.                 case sendMessageTask:
  1659.                     sendMessage(messageToSend, 
  1660.                         getClass(processStack[argumentsOnStack]),
  1661.                         argumentsOnStack);
  1662.                     if (finalTask == BlockReturnTask)
  1663.                         done = true;
  1664.                     break;
  1665.  
  1666.                 case sendSuperTask:
  1667.                     sendMessage(messageToSend,
  1668.                         basicAt(methodCache[hash].methodClass, superClassInClass),
  1669.                         argumentsOnStack);
  1670.                     if (finalTask == BlockReturnTask)
  1671.                         done = true;
  1672.                     break;
  1673.  
  1674.  
  1675.                 case ContextExecuteTask:
  1676.                     contextobj = messageToSend;
  1677.                     executeFromContext(contextobj,
  1678.                         argumentsOnStack);
  1679.                     decr(contextobj);
  1680.                     if (finalTask == ReturnTask)
  1681.                         processStack[processStackTop++] = returnedObject;
  1682.                     else
  1683.                         done = true;
  1684.                     break;
  1685.  
  1686.                 case BlockCreateTask:
  1687.                     /* block is in returnedObject, we just add */
  1688.                     /* context info  but first we must */
  1689.                     /* create the context */
  1690.                     argobj = newArray(argumentsize);
  1691.                     newContext(method, methodClass, &contextobj, argobj, &tempobj);
  1692.                     for (i = 1; i <= argumentsize; i++) {
  1693.                         basicAtPut(argobj, i, processStack[argumentPosition + i - 1]);
  1694.                         }
  1695.                     for (i = 1; i <= temporarySize; i++) {
  1696.                         basicAtPut(tempobj, i, processStack[temporaryPosition + i - 1]);
  1697.                         }
  1698.                     basicAtPut(returnedObject, contextInBlock, contextobj);
  1699.                     processStack[processStackTop++] = returnedObject;
  1700.                     /* we now execute using context - */
  1701.                     /* so that changes to temp will be */
  1702.                     /* recorded properly */
  1703.                     executeFromContext(contextobj, bytecounter);
  1704.                     while (processStackTop > argumentPosition) {
  1705.                         decr(processStack[--processStackTop]);
  1706.                         processStack[processStackTop] = nilobj;
  1707.                         }
  1708.  
  1709.                     /* if it is a block return, */
  1710.                     /* see if it is our context */
  1711.                     /* if so, make into a simple return */
  1712.                     /* otherwise pass back to caller */
  1713.                     /* we can decr, since only nums are */
  1714.                     /* important */
  1715.                     decr(contextobj);
  1716.                     if (finalTask == BlockReturnTask) {
  1717.                         if (blockReturnContext != contextobj)
  1718.                             return;
  1719.                         }
  1720.                     finalTask = ReturnTask;
  1721.                     /* fall into return code */
  1722.  
  1723.                 case ReturnTask:
  1724.                     while (processStackTop > argumentPosition) {
  1725.                         decr(processStack[--processStackTop]);
  1726.                         processStack[processStackTop] = nilobj;
  1727.                         }
  1728.                     /* note that ref count is picked up */
  1729.                     /* from the interpreter */
  1730.                     processStack[processStackTop++] = returnedObject;
  1731.                     done = true;
  1732.                     break;
  1733.  
  1734.                 default:
  1735.                     sysError("unknown task","in sendMessage");
  1736.                 }
  1737.             }
  1738.         }
  1739. /*fprintf(stderr,"returning from message %s\n", charPtr(message));*/
  1740. }
  1741.  
  1742. /*
  1743.     execute from a context rather than from the process stack
  1744. */
  1745. executeFromContext(context, bytecounter)
  1746. object context;
  1747. int bytecounter;
  1748. {    object method, methodclass, arguments, temporaries;
  1749.     boolean done = false;
  1750.  
  1751.     method = basicAt(context, methodInContext);
  1752.     methodclass = basicAt(context, methodClassInContext);
  1753.     arguments = basicAt(context, argumentsInContext);
  1754.     temporaries = basicAt(context, temporariesInContext);
  1755.  
  1756.     while (! done) {
  1757.         execute(method, bytecounter, processStack, processStackTop,
  1758.             memoryPtr(arguments), memoryPtr(temporaries));
  1759.         bytecounter = finalByteCounter;
  1760.         processStackTop = finalStackTop;
  1761.  
  1762.         switch(finalTask) {
  1763.             case sendMessageTask:
  1764.                 sendMessage(messageToSend, 
  1765.                     getClass(processStack[argumentsOnStack]),
  1766.                     argumentsOnStack);
  1767.                 if (finalTask == BlockReturnTask)
  1768.                     done = true;
  1769.                 break;
  1770.  
  1771.             case sendSuperTask:
  1772.                 sendMessage(messageToSend,
  1773.                     basicAt(methodclass, superClassInClass),
  1774.                     argumentsOnStack);
  1775.                 if (finalTask == BlockReturnTask)
  1776.                     done = true;
  1777.                 break;
  1778.  
  1779.             case BlockCreateTask:
  1780.                 /* block is in returnedObject already */
  1781.                 /* just add our context to it */
  1782.                 basicAtPut(returnedObject, contextInBlock, context);
  1783.                 processStack[processStackTop++] = returnedObject;
  1784.                 break;
  1785.  
  1786.             case BlockReturnTask:
  1787.                 blockReturnContext = context;
  1788.                 /* fall into next case and return */
  1789.  
  1790.             case ReturnTask:
  1791.                 /* exit and let caller handle it */
  1792.                 done = true;
  1793.                 break;
  1794.     
  1795.             default:
  1796.                 sysError("unknown task","in context execute");
  1797.         }
  1798.     }
  1799. }
  1800.  
  1801. flushstack()
  1802. {
  1803.     while (processStackTop > 0) {
  1804.         decr(processStack[--processStackTop]);
  1805.         processStack[processStackTop] = nilobj;
  1806.         }
  1807. }
  1808.  
  1809. static interpush(interp, value)
  1810. object interp, value;
  1811. {
  1812.     int stacktop;
  1813.     object stack;
  1814.  
  1815.     stacktop = 1 + intValue(basicAt(interp, stackTopInInterpreter));
  1816.     stack = basicAt(interp, stackInInterpreter);
  1817.     basicAtPut(stack, stacktop, value);
  1818.     basicAtPut(interp, stackTopInInterpreter, newInteger(stacktop));
  1819. }
  1820.  
  1821. object doInterp(interpreter)
  1822. object interpreter;
  1823. {    object context, method, arguments, temporaries, stack;
  1824.     object prev, contextobj, obj, argobj, class, newinterp, tempobj;
  1825.     int i, hash, argumentSize, bytecounter, stacktop;
  1826.  
  1827.     context = basicAt(interpreter, contextInInterpreter);
  1828.     method = basicAt(context, methodInContext);
  1829.     arguments = basicAt(context, argumentsInContext);
  1830.     temporaries = basicAt(context, temporariesInContext);
  1831.     stack = basicAt(interpreter, stackInInterpreter);
  1832.     stacktop = intValue(basicAt(interpreter, stackTopInInterpreter));
  1833.     bytecounter = intValue(basicAt(interpreter, byteCodePointerInInterpreter));
  1834.  
  1835.     execute(method, bytecounter, memoryPtr(stack), stacktop,
  1836.         memoryPtr(arguments), memoryPtr(temporaries));
  1837.     basicAtPut(interpreter, stackTopInInterpreter, newInteger(finalStackTop));
  1838.     basicAtPut(interpreter, byteCodePointerInInterpreter, newInteger(finalByteCounter));
  1839.  
  1840.     switch(finalTask) {
  1841.         case sendMessageTask:
  1842.         case sendSuperTask:
  1843.             /* first gather up arguments */
  1844.             argumentSize = finalStackTop - argumentsOnStack;
  1845.             argobj = newArray(argumentSize);
  1846.             for (i = argumentSize; i >= 1; i--) {
  1847.                 obj = basicAt(stack, finalStackTop);
  1848.                 basicAtPut(argobj, i, obj);
  1849.                 basicAtPut(stack, finalStackTop, nilobj);
  1850.                 finalStackTop--;
  1851.                 }
  1852.  
  1853.             /* now go look up method */
  1854.             if (finalTask == sendMessageTask)
  1855.                 class = getClass(basicAt(argobj, 1));
  1856.             else 
  1857.                 class = basicAt(basicAt(context, 
  1858.                     methodClassInContext), superClassInClass);
  1859.             hash = (messageToSend + class) % ProcessCacheSize;
  1860.             method = findMethod(hash, messageToSend, class);
  1861.  
  1862.             if (method == nilobj) {
  1863.                 /* didn't find it, change message */
  1864.                 incr(argobj);    /* get rid of old args */
  1865.                 decr(argobj);
  1866.                 argobj = newArray(3);
  1867.                 basicAtPut(argobj, 1, smallobj);
  1868.                 basicAtPut(argobj, 2, class);
  1869.                 basicAtPut(argobj, 3, messageToSend);
  1870.                 class = getClass(smallobj);
  1871.                 messageToSend = newSymbol("class:doesNotRespond:");
  1872.                 hash = (messageToSend + class) % ProcessCacheSize;
  1873.                 method = findMethod(hash, messageToSend, class);
  1874.                 if (method == nilobj)    /* oh well */
  1875.                     sysError("cant find method",charPtr(messageToSend));
  1876.                 }
  1877.             newContext(method, methodCache[hash].methodClass, &contextobj, argobj, &tempobj);
  1878.             basicAtPut(interpreter, stackTopInInterpreter, newInteger(finalStackTop));
  1879.             argumentsOnStack = 0;
  1880.             /* fall into context execute */
  1881.  
  1882.         case ContextExecuteTask:
  1883.             if (finalTask == ContextExecuteTask) {
  1884.                 contextobj = messageToSend;
  1885.                 }
  1886.             newinterp = allocObject(InterpreterSize);
  1887.             setClass(newinterp, intrclass);
  1888.             basicAtPut(newinterp, contextInInterpreter, contextobj);
  1889.             basicAtPut(newinterp, previousInterpreterInInterpreter, interpreter);
  1890.             /* this shouldn't be 15, but what should it be?*/
  1891.             basicAtPut(newinterp, stackInInterpreter, newArray(15));
  1892.             basicAtPut(newinterp, stackTopInInterpreter, newInteger(0));
  1893.             basicAtPut(newinterp, byteCodePointerInInterpreter, newInteger(argumentsOnStack));
  1894.             decr(contextobj);
  1895.             return(newinterp);
  1896.             break;
  1897.  
  1898.         case BlockCreateTask:
  1899.             basicAtPut(returnedObject, contextInBlock, context);
  1900.             prev = basicAt(interpreter, creatingInterpreterInInterpreter);
  1901.             if (prev == nilobj)
  1902.                 prev = interpreter;
  1903.             basicAtPut(returnedObject, creatingInterpreterInBlock, prev);
  1904.             interpush(interpreter, returnedObject);
  1905.             decr(returnedObject);
  1906.             return(interpreter);
  1907.  
  1908.         case BlockReturnTask:
  1909.             interpreter = basicAt(interpreter, creatingInterpreterInInterpreter);
  1910.             /* fall into return task */
  1911.  
  1912.         case ReturnTask:
  1913.             prev = basicAt(interpreter, previousInterpreterInInterpreter);
  1914.             if (prev != nilobj) {
  1915.                 interpush(prev, returnedObject);
  1916.                 }
  1917.             /* get rid of excess ref count */
  1918.             decr(returnedObject);
  1919.             return(prev);
  1920.  
  1921.         default:
  1922.             sysError("unknown final task","doInterp");
  1923.         }
  1924.     return(nilobj);
  1925. }
  1926. End
  1927.