v13i056: New release of little smalltalk, Part04/05

Rich Salz rsalz at bbn.com
Tue Feb 23 10:08:57 AEST 1988


Submitted-by: Tim Budd <budd at MIST.CS.ORST.EDU>
Posting-number: Volume 13, Issue 56
Archive-name: little-st2/part04

#!/bin/sh
#
# 
# This is version 2.02 of Little Smalltalk, distributed in five parts.
# 
# This version is dated 12/25/87
# 
# Several bugs and many features and improvements have been made since the
# first posting to comp.src.unix.  See the file ``todo'' for a partial list.
# 
# Comments, bug reports, and the like should be submitted to:
# 	Tim Budd
# 	Smalltalk Distribution
# 	Department of Computer Science
# 	Oregon State University
# 	Corvallis, Oregon
# 	97330
# 
# 	budd at cs.orst.edu
# 	{hp-pcd, tektronix}!orstcs!budd
# 
#
echo 'Start of small.v2, part 04 of 05:'
echo 'x - at.ms'
sed 's/^X//' > at.ms << '/'
X.LP
X(note: this is the first of a series of essays descriging how various 
Xfeatures of the Little Smalltalk bytecodes work).
X.SH
XWhere It's At
X.PP
XThis short note explains how the messages \fBat:\fP, \fBat:put:\fP, and their 
Xrelatives are defined and used in collections.  We start by discussing the 
Xsimplest form of collections, arrays and strings.
X.PP
XThe message \fBat:\fP is not defined anywhere in class \fBArray\fP or any of
Xits subclasses.  Instead, this message is inherited from 
Xclass \fBCollection\fP, which defines it using the following method:
X.DS I
X\fBat:\fP index
X	\(ua self at: index
X		ifAbsent: [ smalltalk error: 'index to at: illegal' ]
X.DE
X.PP
XThe functioning of the message \fBerror:\fP is the topic of another essay;
Xit is sufficient for our purposes to note only that this message prints out
Xthe error string and returns nil.  By redefining \fBat:\fP in this fashion,
Xthe subclasses of \fBCollection\fP need not be concerned about how to deal
Xwith errors in cases where no error recovery action has been specified.
X.PP
XFor an array, an index is out of bounds if it is either less than 1 or greater
Xthan the size of the array.  This is tested by a method in class \fBArray\fP:
X.DS I
X\fBincludesKey:\fP index
X	^ index between: 1 and: self size
X.DE
X.PP
XThe message \fBsize\fP is defined in class \fBArray\fP in terms of the
Xmessage \fBbasicSize\fP
X.DS I
X\fBsize\fP
X	^ self basicSize
X.DE
X.PP
XThe message \fBbasicSize\fP (as well as \fBbasicAt:\fP, discussed below) 
Xis inherited from class 
X\fBObject\fP.  It can be used on any object; on non-arrays it returns
Xthe number of instance variables for the object.  The messages \fBbasicSize\fP 
Xand \fBbasicAt:put:\fP can be used by system
Xclasses, for example debuggers, to access instance variables in an object 
Xwithout having explicit access to the instance variables.  One must be 
Xcareful, however,
X\fBbasicAt:\fP produces a system error, and not a Smalltalk error message,
Xif it is given an index value that is out of range.
X.PP
XUsing \fBincludesKey:\fP for a test, a value is only accessed if the index
Xis legal.  The following method appears in class \fBArray\fP:
X.DS I
X\fBat:\fP index \fBifAbsent:\fP exceptionBlock
X	^ (self includesKey: index)
X		ifTrue: [ self basicAt: index ]
X		ifFalse: [ exceptionBlock value ]
X.DE
X.PP
XA subclass of \fBArray\fP is the class \fBByteArray\fP.  A byte array is a form
Xof array in which the elements can only take on values from zero to 255, or
Xto put it another way, values that can be stored in one byte.
XOn most 16 bit machines, we can store two such bytes in the space it takes
Xto store one object pointer.  Thus, the message \fBsize\fP is redefined
Xin class \fBByteArray\fP as follows:
X.DS I
X\fBsize\fP
X	\(ua self basicSize * 2
X.DE
X.LP
XNote that this implies that byte arrays always have an even number of
Xelements.  Next the message \fBbasicAt:\fP is redefined to use a byte,
Xinstead of object, form of index.  This is accomplished using a primitive
Xmethod, (the message \fBbasicAt:\fP is handled in a similar fashion in
Xclass \fBObject\fP, only using a different primitive).
X.DS I
X\fBbasicAt:\fP index
X	\(ua <26 self index>
X.DE
X.PP
XLike a byte array, a string can also store two byte values in the space
Xit takes to store a single object pointer.  Unlike a byte array, however,
Xa string can be any length, not just an even length.  Therefore the message
X\fBsize\fP is redefned in class \fBString\fP, a subclass of \fBByteArray\fP.
X.DS I
X\fBsize\fP
X	\(ua <14 self>
X.DE
X.PP
XAnother difference between a string and a byte array is that the value
Xreturned by a string must be a character, not an integer.  Therefore
X\fBbasicAt:\fP must also be redefined.  By using the message \fBbasicAt:\fP
Xdefined in \fBByteArray\fP, (the superclass of String, and therefore accessible
Xvia the pseudo variable \fBsuper\fP) the method can obtain the integer value 
Xof the appropriate character.  This value is then used to create a new
Xinstance of class \fBChar\fP:
X.DS I
X\fBbasicAt:\fP index
X	\(ua Char new; value: (super basicAt: index)
X.DE
X.PP
XA value is placed into an array using the message \fPat:put:\fP.  As with 
X\fBat:\fP, a value should only be placed if the index represents a legal
Xsubscript.  This is checked in the following method:
X.DS I
X\fBat:\fP index \fBput:\fP value
X	(self includesKey: index)
X		ifTrue: [ self basicAt: index put: value ]
X		ifFalse: [ smalltalk error: 
X			'illegal index to at:put: for array' ]
X.DE
X.PP
XAs was the case with \fBbasicAt:\fP, one version of \fBbasicAt:put:\fP,
Xto be used by arrays of objects, is defined as part of class \fBObject\fP.
XA different version is found in class \fBByteArray\fP.  Finally a third 
Xversion, which first checks to see if the argument is a Character, is found
Xin class \fBString\fP.
X.DS I
X\fBat:\fP index \fBput:\fP aValue
X	(aValue isMemberOf: Char)
X		ifTrue: [ super basicAt: index put: aValue asciiValue ]
X		ifFalse: [ smalltalk error:
X			'cannot put non Char into string' ]
X.DE
X.SH
XExercises
X.IP 1.
XDescribe the sequence of messages used to respond to the following:
X.DS B
Xx \(<- #(1 2 3) at: 2
X.DE
X.IP 2.
XDescribe how the execution of the above expression could be speeded up by
Xadding new methods.  Note if your methods are specific to arrays of objects,
Xarrays of bytes, or strings.
/
echo 'x - general.ms'
sed 's/^X//' > general.ms << '/'
X.\" information on Little Smalltalk, version 2, beta release
X.SH
XGeneral Overview
X.PP
XFirst, the obvious facts.  This is not Smalltalk-80, nor even Smalltalk-V.
XThis is the second version of the Little Smalltalk system, the first version
Xof which is described in the book recently published by Addison-Wesley*.
X.FS
X* \fIA Little Smalltalk\fP, by Timothy A. Budd.  Published by Addison
XWesley, 1987.  In better bookshops everywhere.
X.FE
XVersion two is smaller and faster; does more in Smalltalk, less in C; and is
Xdesigned to be more portable to a wider variety of machines (we are working
Xon versions now for various PCs).  
X.PP
XMy attitude towards the language has been
Xrather cavalier; what I liked I kept and what I didn't like I tossed out.
XThis is explained in more detail in my book and in the end of this note.
XAs a consequence, individuals familiar with ST-80 or Smalltalk-V will be struck 
Xby how much they are missing, and I make no apologies for this.  On the
Xother hand, you don't find Smalltalk-V posted to comp.source.unix.  
XAmong the features
Xyou won't find here are metaclasses, class methods, windows, graphics 
Xsupport, and more.
X.PP
XWhat you will find is a small language that does give you the flavor of
Xobject oriented programming at very little cost.  We are working to improve
Xthe system, and hope to distribute new versions as we develop them, 
Xas well as porting it to a wide range of machines.
XIf you find (and preferably, fix!) bugs let us know.
XIf you make nice additions let us know.
XIf you want to make complements let us know.
XIf you want to make complaints let us know.
XIf you want support you just might be out of luck.
X.PP
XThis software is entirely public domain.  You are encouraged to give it
Xto as many friends as you may have.  As a courtesy, I would appreciate it
Xif you left my name on the code as the author, but I make no other claims
Xto it (I also, of course, disavow any liability for any bizarre things you
Xmay choose to do with it).  Enjoy.
X.SH
XBuilding the System
X.PP
XThe first step in building the system is to unpack the sources.
XThe fact that you are reading this means you have probably already figured
Xout how to do this.
X.PP
XThere are various different types of files sent with the distribution.
XFiles ending in .c and .h are C sources, for both the parser and the bytecode
Xinterpreter.  Files ending in .ms are manuscripts, in troff format using
Xthe ms macro package (this file is a good example).  Files ending in .st
Xare smalltalk sources, compiled by the parser to make the initial object
Ximage.  Finally, there are a few small files that don't fall into any
Xcategory, BUGS for listing notable bugs, the make file, and so on.
X.PP
XThe next step is to tailor the system to the type of enviornment it will be
Xrun in.
XFor most users, this should mean only changing at most three lines in the
Xfile env.h.  These three lines are near the front of the file and are
Xclearly marked.  Two are hard paths; for the default initial object image
Xand for a temporary file to be used when editing.  The third line is a
X``meta-define'' which indicates the type of machine and/or operating system
Xto be used.  You should examine the rest of the file to see the variety of
Xsystems supported.  If you are unable to find anything appropriate, you will
Xhave to look in the document install.ms for further instructions.  In this
Xlatter case, if you are sucessful in porting the software to a new machine,
XI would be pleased if you could let me know the nature of the changes
Xrequired.
X.PP
XOnce you have tailored the system, there are then
Xthree steps involving in building the system; making the parser
X(the component used to generate the initial object image), making the
Xbytecode interpreter, and making the object image.  Typing \fImake\fP, with
Xno arguments, will do all three.  For more detailed instructions on making
Xthe system consult install.ms.
X.PP
XOnce you have sucessfully created the parser, the bytecode compiler, and
Xan object image, type 
X.DS I
Xst
X.DE
X.LP
Xto run the system.
XNow would be a very good time to go read explore.ms, which would tell you
Xmore how to find your way around.
X.SH
XChanges from Little Smalltalk version one
X.PP
XThe following changes have been made from version one to version two:
X.IP \(bu
XThe user interface is slightly different.  This is most apparent in the way
Xnew classes are added (see explore.ms), and in the fact that expressions will
Xnot be printed unless you explicitly request printing, and in the fact that
Xnew global variables cannot be created at the command level merely by
Xassignment.
X.IP \(bu
XMuch (very much) more of the system is now written in Smalltalk, rather
Xthan C.  This allows the user to see, and modify it if they wish.
XThis also means that the virtual machine is now much smaller.
X.IP \(bu
XThe pseudo variable selfProcess is no longer supported.
XThe variables true, false and nil are now treated as global variables, not
Xpseudo variables (see below).
XThere are plans for adding processes to version two, but they have not
Xbeen formalized yet.
X.IP \(bu
XGlobal variables are now supported; in fact classes are now simply global
Xvariables, as are the variables true, false, smalltalk and nil.
XThe global variable globalNames contains the dictionary of all currently
Xknown global variables and their values.
X(Pool variables are still not supported).
X.IP \(bu
XNumbers are a little more robust.  If integer operations overflow, they are
Xautomatically converted into floating point numbers.  This is under control
Xof Smalltalk code, so if I (or, preferably, somebody else) ever get around
Xto implementing infinite precision numbers, they can easily be added in.
X.IP \(bu
XThe internal bytecodes are slightly different.  In particular, the bytecode
Xrepresenting ``send to super'' has been eliminated, and a bytecode representing
X``do a primitive'' has been added.
X.IP \(bu
XThe internal representation of objects is different.  Instead of the
X``super-object'' chain, objects are now created big enough to hold all the
Xinstance variables for all their superclasses.  (This is the way it is done
Xin Smalltalk-80, and, to the best of my knowledge, in Smalltalk-V).
X.IP \(bu
XThe Collection hierarchy has been rearranged.  The rational for this change
Xis explained in more detail in another essay.
X(possibly not written yet).
X.IP \(bu
XSome methods, most notably the error message methods, have been moved out
Xof class Object and into class Smalltalk.
X.IP \(bu
XThe syntax for primitives is different; the keyword \fBprimitive\fP has been
Xeliminated, and named primitives are now gone as well.
XFewer actions are performed by primitives, having been
Xreplaced by Smalltalk methods.
X.IP \(bu
XCommand line options, such as the fast load feature, have been eliminated.
XHowever, since version two reads in a binary object image, not a textual
Xfile, loading should be considerably faster.
X.SH
XElectronic Communication
X.PP
XHere is my address, various net addresses:
X.DS I
XTim Budd
XOregon State University
XDepartment of Computer Science
XCorvallis, Oregon 97331 USA
X(503) 754-3273
X
Xbudd@ cs.orst.edu
X
X{tektronix, hp-pcd} !orstcs!budd
X.DE
X.SH
XChanges
X.PP
XI want to emphasize that this is not even a beta-test version (does that
Xmake it an alpha or a gamma version?).  I will be making a number of
Xchanges, hopefully just additions to the initial image, in the next
Xfew months.  In addition, I hope to prepare versions for other machines,
Xnotably the Macintosh and the IBM PC.  I am also encouraging others to
Xport the system to new machines.  If you have done so, please let me
Xknow.
/
echo 'x - install.ms'
sed 's/^X//' > install.ms << '/'
X.SH
XInstalling Little Smalltalk
X.PP
XFor most users the following simple steps should suffice to build the
Xsystem.
X.IP \(bu
XUnpack the sources.
X.IP \(bu
XEdit the file env.h, changing the metadefine to an already known system
Xtype.
X.IP \(bu
XType \fImake\fP.
X.PP
XThere are a few systems for which more extensive work is required;
Xand of course porting the system to new environments may entail
Xconsiderable work.
XThe remainder of this document is intended to provide more detailed
Xinformation for the curious, and to help those individuals for whom the
Xprevious instructions don't work.
X.SH
XIBM PC and Turbo C
X.PP
XFor these systems there are two small changes that are required, in addition
Xto those noted above.  In the method for saveImage, class Smalltalk, file
Xunix.c, the mode for the open statement must be 'wb', not 'w'.
XAnd in method delete, class File, file unix.c, the command used to delete
Xfiles should be ``DEL'', not ``rm''.
XIt may also be necessary to redefine the value of the global variable
X\fIeditor\fP (set by the script found in file script.ini, used when making
Xthe initial image).  Also the name of a temporary file (found in method
XscratchFile, class File, file unix.st) may have to be changed.
X.SH
XTektronix 4404
X.PP
XThere are several changes that are required to get the system to run on the
X4404.  Start by defining the metadefine constant SYSV in env.h.
XNext, change the Makefile in the following ways,
X.IP \(bu
XTranslate all the .o extensions to .r .
X.IP \(bu
XRemove any CFLAGS definitions.
X.IP \(bu
XChange the rules for parse and st to use +o= notation, which must appear at
Xthe end.  Also add a call on headset, setting the program size to at least
X512K (more if you have it).
X.DS I
Xst: \fIfile names\fP
X	cc \fIfile names\fP +o=st
X	headset st +b=512K
X.DE
X.SH
XPorting to new systems
X.PP
XThere are many ways in which compilers and operating systems differ 
Xfrom each other.
XA fair amount of work has been expanded in making sure the software will
Xoperate on most machines, which requires that different code fragements be
Xused on different systems.  In large part these are controlled by a single
X``meta-define'' in the file env.h.  Setting this one value then causes the
Xexpansion of another code segment, which then defines many more options.
X.PP
XIn the event that you are attempting to port the software to a system that
Xhas not previously been defined, you will need to decide which set of
Xoptions to enable.  The next two sections contain information you may need
Xin making this determination.
X.SH
XDefine Options
X.PP
XMany options are specified merely by giving or not giving a DEFINE
Xstatement in the file env.h.  The following table presents the meaning for
Xeach of these values:
X.de Op
X.IP \\fB\\$1\\fP
X.br
X..
X.Op ALLOC
XDefined If there is an include file called alloc.h which defines calloc, 
Xmalloc, and the like.
X.Op BINREADWRITE
XDefined if the fopen specification for binary files must include the "b"
Xmodifier.  This is true on many MS-DOS inspired systems.
X.Op NOENUMS
XDefined if enumerated datatypes are not supported.  If defined, these will
Xbe replaced by #define constants.
X.Op NOTYPEDEF
XDefined if the typedef construct is not supported.  If defined, these will
Xbe replaced by #define constructs.
X.Op NOVOID
XDefined if the void keyword is not recognized.
XIf defined, expect \fIlint\fP to complain a lot about functions returning
Xvalues which are sometimes (or always) ignored.
X.Op SIGNALS
XUsed if \fIboth\fP the <signals.h> package and the <longjmp.h> package are
Xavilable, and if the routine used to set signals is signal.
XIncompatible with \fBSSIGNALS\fP.
X.Op SSIGNALS
XUsed if \fIboth\fP the <signals.h> package and the <longjmp.h> package are
Xavailable, and if the routine used to set signals is ssignal.
XIncompatible with \fBSIGNALS\fP.
X.Op STRING
XUsed if the string functions (strcpy, strcat and the like) are found in
X<string.h>.  This switch is incompatible with \fBSTRINGS\fP.
X.Op STRINGS
XUsed if the string functions (strcpy, strcat and the like) are found in
X<strings.h>.  This switch is incompatible with \fBSTRING\fP.
X.LP
XIn addition, several routines can optionally be replaced by macros for
Xgreater efficiency.  See the file memory.h for more information.
X.SH
XObject Memory
X.PP
XThere are several datatypes, not directly supported by C, that are used in
Xthe Little Smalltalk system.  The first of these is the datatype byte.
XA byte is an eight bit unsigned (hence positive) quantity.
XOn many systems the appropriate datatype is unsigned char, however on other
Xsystems this declaration is not recognized and other forms may be required.
XTo aid in coverting to and from bytes the macro byteToInt() is used, which
Xconverts a byte value into an integer.  In addition, the routines byteAt
Xand byteAtPut are used to get and put bytes from byte strings.
X.PP
XThe other datatype is that used to represent object points.  On most
Xmachines in which a short is 16 bits, the datatype short should suffice.
XMuch more information on the memory module can be found in the file
Xmemory.h.
X.SH
XOptions in Building the System
X.PP
XTo create the parser type
X.DS I
Xmake parse
X.DE
X.PP
XThe resulting program, called parse, is used to generate the object image
Xinitially loaded into the bytecode interpreter.
X.PP
XNext, make the interpreter itself by typing
X.DS I
Xmake st
X.DE
X.PP
XNote that the interpreter and the parser share some files.
X.PP
XFinally, produce an initial object image.  The image created when you type
X.DS I
Xmake sunix
X.DE
X.LP
Xis the smallest and fastest.  It is a single process version of smalltalk.
XA slower multiprocess version can be created by typing ``make munix''*.
X.FS
X* Multi processing from munix is done entirely in Smalltalk.
XWhile this is a good idea from the point of view of keeping the bytecode
Xinterpreter small and giving one the greatest flexibility, there seems to
Xbe a dramatic performance penalty.  I'm considering the alternatives.
X.FE
XOf more interest, an image containing test cases
Xcan be generated by typing ``make stest''.
XThis command compiles several test cases, then runs st on a script which
Xinvokes all the test cases.  There is a similar command mtest for the
Xmultiprocessing version.
X.PP
XOnce you have created an object image, type 
X.DS I
Xst
X.DE
X.LP
Xto run the system.
XBy default the image file ``imageFile'' is read.  You can optionally
Xuse a different image file by giving the name on the command line following
Xthe st command.
X.SH
XCompiler Bogosities
X.PP
XThis section will detail some of the unnatural actions you may have to
Xperform to get Little Smalltalk to work with a few brain damaged compilers.
XSince there are so few of these, it I see it as a problem more with the
Xcompilers than with the system, the code make no accomodation for these.
X.PP
XOn some older Sequent Balance C compilers, incorrect code is produced for
X.DS I
Xhash %= (objectSize(symbols) / 2)
X.DE
X.LP
XIn the file image.c.  This should be replaced by
X.DS I
Xhash = hash % (objectSize(symbols) / 2)
X.DE
X.PP
XOn many systems external names are restricted to six (or even five!)
Xcharacters.  This causes significant problems.  I have heard of systems
Xwhich automatically generate a sed script to take care of this, but have
Xnot found one yet.  If you know of such a thing let me know.
X.SH
XHelping Others
X.PP
XIf you succeed in getting Little Smalltalk ported to a machine or operating
Xsystem not described in env.h, I would be most pleased to get a listing of
Xthe changes you made.  These can then be incorporated into the latest
Xdistribution.
/
echo 'x - lex.c'
sed 's/^X//' > lex.c << '/'
X/*
X	Little Smalltalk, version 2
X	Written by Tim Budd, Oregon State University, July 1987
X
X	lexical analysis routines for method parser
X	should be called only by parser 
X*/
X
X# include <stdio.h>
X# include <ctype.h>
X# include "env.h"
X# include "memory.h"
X# include "lex.h"
X
Xextern double atof();
X
X/* global variables returned by lexical analyser */
X
Xtokentype token;		/* token variety */
Xchar tokenString[80];		/* text of current token */
Xint tokenInteger;		/* integer (or character) value of token */
Xdouble tokenFloat;		/* floating point value of token */
X
X/* local variables used only by lexical analyser */
X
Xstatic char *cp;		/* character pointer */
Xstatic char pushBuffer[10];	/* pushed back buffer */
Xstatic int  pushindex;		/* index of last pushed back char */
Xstatic char cc;			/* current character */
Xstatic long longresult;		/* value used when building int tokens */
X
X/* lexinit - initialize the lexical analysis routines */
Xnoreturn lexinit(str)
Xchar *str;
X{
X	pushindex = 0;
X	cp = str;
X	/* get first token */
X	ignore nextToken();
X}
X
X/* pushBack - push one character back into the input */
Xstatic pushBack(c)
Xchar c;
X{
X	pushBuffer[pushindex++] = c;
X}
X
X/* nextChar - retrieve the next char, from buffer or input */
Xstatic char nextChar()
X{
X	if (pushindex > 0) cc = pushBuffer[--pushindex];
X	else if (*cp) cc = *cp++;
X	else cc = '\0';
X	return(cc);
X}
X
X/* isClosing - characters which can close an expression */
Xstatic boolean isClosing(c)
Xchar c;
X{
X	switch(c) {
X		case '.': case ']': case ')': case ';':
X			return(true);
X	}
X	return(false);
X}
X
X/* isSymbolChar - characters which can be part of symbols */
Xstatic boolean isSymbolChar(c)
Xchar c;
X{
X	if (isdigit(c) || isalpha(c)) return(true);
X	if (isspace(c) || isClosing(c)) return(false);
X	return(true);
X}
X
X/* singleBinary - binary characters that cannot be continued */
Xstatic boolean singleBinary(c)
Xchar c;
X{
X	switch(c) {
X		case '[': case '(': case ')': case ']':
X			return(true);
X	}
X	return(false);
X}
X
X/* binarySecond - return true if char can be second char in binary symbol */
Xstatic boolean binarySecond(c)
Xchar c;
X{
X	if (isalpha(c) || isdigit(c) || isspace(c) || isClosing(c) ||
X		singleBinary(c))
X		return(false);
X	return(true);
X}
X
Xtokentype nextToken()
X{	char *tp;
X	boolean sign;
X
X	/* skip over blanks and comments */
X	while(nextChar() && (isspace(cc) || (cc == '"')))
X		if (cc == '"') {
X			/* read comment */
X			while (nextChar() && (cc != '"')) ;
X			if (! cc) break;	/* break if we run into eof */
X			}
X
X	tp = tokenString;
X	*tp++ = cc;
X
X	if (! cc)			/* end of input */
X		token = inputend;
X	
X	else if (isalpha(cc)) {		/* identifier */
X		while (nextChar() && isalnum(cc))
X			*tp++ = cc;
X		if (cc == ':') {
X			*tp++ = cc;
X			token = namecolon;
X			}
X		else {
X			pushBack(cc);
X			token = nameconst;
X			}
X		}
X
X	else if (isdigit(cc)) {		/* number */
X		longresult = cc - '0';
X		while (nextChar() && isdigit(cc)) {
X			*tp++ = cc;
X			longresult = (longresult * 10) + (cc - '0');
X			}
X		if (longCanBeInt(longresult)) {
X			tokenInteger = longresult;
X			token = intconst;
X			}
X		else {
X			token = floatconst;
X			tokenFloat = (double) longresult;
X			}
X		if (cc == '.') {	/* possible float */
X			if (nextChar() && isdigit(cc)) {
X				*tp++ = '.';
X				do
X					*tp++ = cc;
X				while (nextChar() && isdigit(cc));
X				if (cc) pushBack(cc);
X				token = floatconst;
X				*tp = '\0';
X				tokenFloat = atof(tokenString);
X				}
X			else {
X				/* nope, just an ordinary period */
X				if (cc) pushBack(cc);
X				pushBack('.');
X				}
X			}
X		else
X			pushBack(cc);
X
X		if (nextChar() && cc == 'e') {	/* possible float */
X			if (nextChar() && cc == '-') {
X				sign = true;
X				ignore nextChar();
X				}
X			else
X				sign = false;
X			if (cc && isdigit(cc)) { /* yep, its a float */
X				*tp++ = 'e';
X				if (sign) *tp++ = '-';
X				while (cc && isdigit(cc)) {
X					*tp++ = cc;
X					ignore nextChar();
X					}
X				if (cc) pushBack(cc);
X				*tp = '\0';
X				token = floatconst;
X				tokenFloat = atof(tokenString);
X				}
X			else {	/* nope, wrong again */
X				if (cc) pushBack(cc);
X				if (sign) pushBack('-');
X				pushBack('e');
X				}
X			}
X			else
X				if (cc) pushBack(cc);
X		}
X
X	else if (cc == '$') {		/* character constant */
X		tokenInteger = (int) nextChar();
X		token = charconst;
X		}
X
X	else if (cc == '#') {		/* symbol */
X		tp--;	/* erase pound sign */
X		if (nextChar() == '(')
X			token = arraybegin;
X		else {
X			pushBack(cc);
X			while (nextChar() && isSymbolChar(cc))
X				*tp++ = cc;
X			pushBack(cc);
X			token = symconst;
X			}
X		}
X
X	else if (cc == '\'') {		/* string constant */
X		tp--;	/* erase pound sign */
X		strloop:
X		while (nextChar() && (cc != '\''))
X			*tp++ = cc;
X		/* check for nested quote marks */
X		if (cc && nextChar() && (cc == '\'')) {
X			*tp++ = cc;
X			goto strloop;
X			}
X		pushBack(cc);
X		token = strconst;
X		}
X
X	else if (isClosing(cc))		/* closing expressions */
X		token = closing;
X
X	else if (singleBinary(cc)) {	/* single binary expressions */
X		token = binary;
X		}
X
X	else {				/* anything else is binary */
X		if (nextChar() && binarySecond(cc))
X			*tp++ = cc;
X		else
X			pushBack(cc);
X		token = binary;
X		}
X
X	*tp = '\0';
X	return(token);
X}
/
echo 'x - names.c'
sed 's/^X//' > names.c << '/'
X/*
X	Little Smalltalk, version 2
X	Written by Tim Budd, Oregon State University, July 1987
X
X	Name Table module
X
X	A name table is the term used for a Dictionary indexed by symbols.
X	There are two name tables used internally by the bytecode interpreter.
X	The first is the table, contained in the variable globalNames,
X	that contains the names and values of all globally accessible 
X	identifiers.  The second is the table of methods associated with
X	every class.  Notice that in neither of these cases does the
X	system ever put anything INTO the tables, thus there are only
X	routines here for reading FROM tables.
X
X	(putting things INTO the table is all done in Smalltalk code,
X	using the methods from class Dictionary)
X
X	One complication of instances of class Symbol is that all
X	symbols must be unique, not only so that == will work as expected,
X	but so that memory does not get overly clogged up with symbols.
X	Thus all symbols are kept in a hash table, and when new symbols
X	are created (via newSymbol(), below) they are inserted into this
X	table, if not already there.
X
X	This module also manages the definition of various symbols that are
X	given fixed values for efficiency sake.  These include the objects
X	nil, true, false, and various classes.
X*/
X
X# include <stdio.h>
X# include "env.h"
X# include "memory.h"
X# include "names.h"
X
X/* global variables used to avoid repeated examinations of the global symbol table */
Xobject trueobj = nilobj;	/* the pseudo variable true */
Xobject falseobj = nilobj;	/* the pseudo variable false */
Xobject smallobj = nilobj;	/* the pseudo variable smalltalk */
Xobject arrayclass = nilobj;	/* the class ``Array'' */
Xobject blockclass = nilobj;	/* the class ``Block'' */
Xobject contextclass = nilobj;	/* the class ``Context'' */
Xobject intclass = nilobj;	/* the class ``Integer'' */
Xobject intrclass = nilobj;	/* the class ``Interpreter'' */
Xobject symbolclass = nilobj;	/* the class ``Symbol'' */
Xobject stringclass = nilobj;	/* the class ``String'' */
X
X/*
X	some messages are encoded in concise bytecode format -
Xto reduce the size of the compiled methods
X(also, in some cases, to more efficiently detect special cases
Xhandled in the interpreter, rather than by methods)
X*/
X
Xchar *binStrs[] = {"+", "-", "<", ">", "<=", ">=", "=", "~=", "*", 
X"quo:", "rem:", "bitAnd:", "bitXor:", 
X"==", ",", "at:", "basicAt:", "do:", "coerce:", "error:", "includesKey:",
X"isMemberOf:", "new:", "to:", "value:", "whileTrue:", "addFirst:", "addLast:",
X0};
X
Xobject binSyms[28];
X
Xchar *unStrs[] = {"isNil", "notNil", "new", "value", "class", "size",
X"basicSize", "print", "printString", 0};
X
Xobject unSyms[9];
X
Xchar *keyStrs[] = {"at:ifAbsent:", "at:put:", "basicAt:put:", "between:and:",
X0};
X
Xobject keySyms[4];
X
Xobject nameTableLookup(dict, symbol)
Xobject dict, symbol;
X{	int hash, tablesize;
X	object table, link;
X
X	/* first get hash table */
X	table = basicAt(dict, 1);
X
X	/* now see if table is valid */
X	if ((tablesize = objectSize(table)) < 3)
X		sysError("system error","lookup on null table");
X	else {
X		hash = 3 * ( symbol % (tablesize / 3));
X		if (basicAt(table, hash+1) == symbol)
X			return(basicAt(table, hash+2));
X
X		/* otherwise look along the chain of links */
X		for (link=basicAt(table, hash+3); link != nilobj; 
X					link=basicAt(link, 3))
X			if (basicAt(link, 1) == symbol)
X				return(basicAt(link, 2));
X
X	}
X	return (nilobj);
X}
X
Xobject getClass(obj)
Xobject obj;
X{
X	if (isInteger(obj))
X		return(intclass);
X	return (classField(obj));
X}
X
Xstatic object globalGet(name)
Xchar *name;
X{	object newobj;
X
X	newobj = globalSymbol(name);
X	if (newobj == nilobj)
X		sysError("symbol not found in image", name);
X	return(newobj);
X}
X
Xnoreturn initCommonSymbols()
X{	int i;
X
X	trueobj = globalGet("true");
X	falseobj = globalGet("false");
X	smallobj  = globalGet("smalltalk");
X	arrayclass = globalGet("Array");
X	blockclass = globalGet("Block");
X	contextclass = globalGet("Context");
X	intclass = globalGet("Integer");
X	symbolclass = globalGet("Symbol");
X	stringclass = globalGet("String");
X	/* interpreter may or may not be there */
X	intrclass = globalSymbol("Interpreter");
X
X	for (i = 0; i < 28; i++)
X		binSyms[i] = newSymbol(binStrs[i]);
X
X	for (i = 0; i < 9; i++)
X		unSyms[i] = newSymbol(unStrs[i]);
X
X	for (i = 0; i < 4; i++)
X		keySyms[i] = newSymbol(keyStrs[i]);
X}
X
Xobject newArray(size)
Xint size;
X{	object newobj;
X
X	if (arrayclass == nilobj) {
X		arrayclass = globalSymbol("Array");
X		if (arrayclass == nilobj) 
X			sysError("can't find global symbol","Array");
X		}
X	newobj = allocObject(size);
X	setClass(newobj, arrayclass);
X	return(newobj);
X}
X
Xobject newSymbol(str)
Xchar *str;
X{	int hash;
X	object newSym, link;
X	char *p;
X
X	/* first compute hash value of string text */
X	/* this is duplicated in image.c - make sure any changes match there */
X	hash = 0;
X	for (p = str; *p; p++)
X		hash += *p;
X	if (hash < 0) hash = - hash;
X	hash = 2 * ( hash % (objectSize(symbols) / 2));
X
X	/* next look to see if it is in symbols - note that this
X	   text duplicates that found in nameTableLookup, only using
X	   string comparison instead of symbol comparison */
X	newSym = basicAt(symbols, hash+1);
X	if (newSym && streq(str, charPtr(newSym)))
X		return(newSym);
X
X	/* not in table, look along links */
X	for (link=basicAt(symbols, hash+2); link != nilobj; link=basicAt(link,2)) {
X		newSym = basicAt(link, 1);
X		if (streq(str, charPtr(newSym)))
X			return(newSym);
X		}
X
X	/* not found, make a new symbol */
X	newSym = allocSymbol(str);
X	setClass(newSym, symbolclass);
X
X	/* now insert new symbol in table, so next time we will find it */
X	if (basicAt(symbols, hash+1) == nilobj)
X		basicAtPut(symbols, hash+1, newSym);
X	else {		/* insert along links */
X		link = allocObject(2);
X		basicAtPut(link, 1, newSym);
X		basicAtPut(link, 2, basicAt(symbols, hash+2));
X		basicAtPut(symbols, hash+2, link);
X		}
X
X	return(newSym);
X}
X
Xobject newStString(value)
Xchar *value;
X{	object newobj;
X
X	newobj = allocSymbol(value);
X	setClass(newobj, stringclass);
X	return(newobj);
X}
X
Xobject newFloat(d)
Xdouble d;
X{	object newobj;
X
X	newobj = allocFloat(d);
X	setClass(newobj, globalSymbol("Float"));
X	return(newobj);
X}
/
echo 'x - todo'
sed 's/^X//' > todo << '/'
XThings to do
X
XWrite infinite precision number package.
X
XAdd traceback (unfortunately, this must be done in the interpreter, not
Xin Smalltalk code).
X
XAdd descriptions to classes (to be displayed by display)
X
Xflush message cache shouldn't flush everything, just one entry.
X
Xchange process manager to use alloca, if available (this would allow
Xprocesses to run as deep as the process stack, instead of the artificial
Xlimitation now imposed).
X
Xadd methods to class Class so that assigning variables: or changing
Xsuperclass will automatically recompile all methods.
X
XMake files a subclass of collection  (to be useful, requires adding more
Xfunctionality to files).
X
Xmodify the parser so that it can add to an existing image.
X
XDone
X
XClass File now added, changes interface to use files.
XInteger arith ops now trap overflows
XAllowed non-alpha tokens (such as #+ )
XFixed doEdit so that it allows you to retry.
XFixed parser so that optimized messages don't require [ ] around args.
XFixed parser so that empty blocks work correctly
XTraps interrups and restarts gracefully now.
XCreated much better user interface
XFixed bug in lexer, preventing reading past end of input
X
Xmessages added:
X	display (for classes and collections)
X	respondsTo (for symbols)
X	changed indexOf to take a block, rather than a value
X	class addSubClass
X	smalltalk inquire:
/
echo 'x - top.ms'
sed 's/^X//' > top.ms << '/'
X.SH
XWho's On Top?
X.PP
XOne of the most important decisions to be made in designing a new user
Xinterface (or front end) for the Little Smalltalk system is whether the user
Xinterface management code should sit on top of the Smalltalk bytecode 
Xinterpreter, setting up commands and invoking the interpreter to execute them,
Xor underneith the bytecode interpreter, being invoked by Smalltalk, via the
Xmechanism of primitive methods.  Both schemes have advantages and disadvantages
Xwhich we will discuss in this essay.
X.PP
XIn a simple interface, placing Smalltalk on top is often easier.  The main
Xdriver need only set up one initial call to the Smalltalk bytecode interpreter,
Xand thereafter everything is done in Smalltalk.  For example, we might put
Xinitialization code in a method in class \fBSmalltalk\fP, as follows:
X.DS L
XClass Smalltalk
X	getString
X		\(ua <1>
X|
X	run		| string |
X		[ '>	' printNoReturn.
X		   string <- smalltalk getString. 
X		   string notNil ]
X			whileTrue: [ (string size > 0)
X					ifTrue: [ smalltalk doIt: string ] ]
X]
X.DE
X.PP
XOnce the bytecode interpreter is started on the method \fBrun\fP, it will
Xloop continuously, reading commands from the user (via the method 
X\fBgetString\fP) and executing them (via the method \fBdoIt:\fP).
XPresumably the user has some way of indicating end of input, such as the
Xunix control-D convention, which causes \fBgetString\fP to return the
Xvalue nil.  The \fIif\fP statement inside the while loop
Xinsures that if the user simply hits the return key execution will quickly 
Xloop back to the prompt.
X.PP
XBesides making the initialization for the Little Smalltalk system easy,
Xthis approach also has the advantage of putting more code into Smalltalk
Xitself, where the user can see it and (presumably) modify it if they wish.
XA general guideline is that it is better to put as much into Smalltalk
Xas possible, since Smalltalk is easier to write and the bytecode representation
Xusually smaller than the equivalent code in C.
XNever the less, there are valid reasons why an implementor might choose
Xa different technique.
X.PP
XFor example, if there are many other activities which should command the 
Xattention of the controlling program (window updates, mouse motions) the 
XSmalltalk code may not be able to respond fast enough, or might become too 
Xlarge and complex to be workable.
XIn this case the only alternative is to have the front end respond directly
Xto events, and only invoke the Smalltalk interpreter as time permits.
XIn basic terms, the front end would perform the loop written in the method
X\fBinit\fP shown above (along with handling various other tasks), and then 
Xcall upon the method in class \fBSmalltalk\fP
Xto execute the message \fBdoIt:\fP.
X.SH
XHow to Do It
X.PP
XIn either of the two schemes described above, an important message is 
X\fBdoIt:\fP, which takes a string (presumably representing a Smalltalk
Xexpression) and performs it.  An easy way to perform this message is to
Xmake a method out of the expression, by appending a message pattern
Xon front, and then pass the string to the method parser.  If the method
Xparser is successful, the method can then be executed.
X.DS L
XdoIt: aString		| method |
X	method <- Method new.
X	method text: ( 'proceed ', aString ).
X	(method compileWithClass: Smalltalk)
X		ifTrue: [ method executeWith: #( 0 ) ]
X.DE
X.PP
XThe message \fBcompileWithClass:\fP compiles the method as if it was
Xappearing as part of class Smalltalk.  If compilation is successful,
Xthe message \fBexecuteWith:\fP executes the message, using as arguments
Xthe array #(0).  The array that accompanies this message must have at
Xleast one element, as the first value is used as the receiver for
Xthe method.
XSimilar techniques can be used for the message \fBprintIt:\fP, if desired.
X.SH
XThe Other End
X.PP
XThe opposite extreme from the front end are those messages that originate
Xwithin the bytecode interpreter and must be communicated to the user.
XWe can divide these values into four categories:
X.IP 1.
XSystem errors.  These are all funnelled through the routine sysError(), found
Xin memory.c.  System errors are caused by dramatically wrong conditions,
Xand should generally cause the system to abort after printing the message
Xpassed as argument to sysError().
X.IP 2.
XCompiler errors.  As we noted above, the method compiler is used to
Xparse expressions typed directly at the keyboard, so these message can
Xalso arise in that manner.  These are all funnelled through the routine
XcompilError(), found in parse.c.  These should print their arguments 
X(two strings), in an appropriate location on the users screen.
XExecution continues normally after call.
X.IP 3.
XVarious primitives, found in primitive.c, are also used to print strings
Xon the users terminal.  In particular, an appropriate meaning should be
Xgiven to the message \fBprint\fP in class \fBString\fP.  What appropriate
Xmeans is undoubtedly implementation specific.
X.IP 4.
XFinally, and perhaps most importantly, there must be some means provided
Xto allow users to enter and edit methods.  The interface for this task
Xis standard; instances of class \fBClass\fP must respond to the messages
X\fBaddMethod\fP and \fBeditMethod:\fP, the latter taking as argument a
Xsymbol representing the name of a method.  How they achieve their two
Xtasks is, however, implementation specific.
XUnder Unix, a simple implementation adds a new primitive for Strings;
Xthis primitive copies the string into a temporary file, starts up the
Xeditor on the file, and returns the contents of the file when the user
Xexits the editor.  Having this capability, the method editing code
Xcan be given as follows.  In class \fBClass\fP:
X.DS L
X	addMethod
X		self doEdit: ''
X|
X	editMethod: name		| theMethod |
X		theMethod <- methods at: name
X				ifAbsent: [ 'no such method ' print. \(ua nil ].
X		self doEdit: theMethod text
X|
X	doEdit: startingText		| theMethod |
X		theMethod <- Method new;
X			text: startingText edit.
X		(theMethod compileWithClass: self)
X			ifTrue: [ methods at: theMethod name put: theMethod ]
X.DE
X.LP
XAnd in class \fBString\fP:
X.DS L
X	edit
X		\(ua <19 self>
X.DE
X.LP
XHere primitive 19 performs all the tasks of creating the temporary file,
Xstarting the editor, and creating the string representing the file
Xcontents when the editor is exited.
X.PP
XAlternative techniques, for example using windowing, would undoubtedly
Xbe more complicated.
/
echo 'x - unix.st'
sed 's/^X//' > unix.st << '/'
X*
X* Little Smalltalk, version 2
X* Written by Tim Budd, Oregon State University, July 1987
X*
X*  methods for the unix front end - single process version
X*
X*	(override previous declaration, adding new instance variable)
XDeclare Smalltalk Object errorRecoveryBlock
XDeclare File Object name pointer
X*	(better override global variable as well )
XInstance Smalltalk smalltalk
X* 	make global variables for standard files
XInstance File stdin
XInstance File stdout
XInstance File stderr
X*
XClass File
X	asString	| text line |
X		text <- ''.
X		[line <- self getString. line notNil]
X			whileTrue: [ text <- text , line ].
X		^ text
X|
X	name: string
X		name <- string
X|
X	name
X		^ name
X|
X	scratchFile
X		name <- 'junk.tmp'
X|
X	open: mode
X		pointer <- <120 name mode>.
X		pointer isNil
X			ifTrue: [ smalltalk error: 'open failed']
X|
X	close
X		(pointer notNil)
X			ifTrue: [<121 pointer>].
X		pointer <- nil.
X|
X	delete
X		('rm ', name) unixCommand
X|
X	fileIn		| line |
X		[ line <- self getString. line notNil ]
X			whileTrue: [ line <- line words: 
X				[:x | x isAlphabetic ] .
X				     Switch new; key: (line at: 1);
X			ifMatch: 'Class' do: [self fileInClass: line ] ;
X			ifMatch: 'Method' do: 
X					[ self fileInMethod: line ] ;
X				else: [ ^ smalltalk error: 
X					'invalid format for fileIn'] ]
X|
X	fileInClass: commandLine	| name |
X		name <- (commandLine at: 2
X			ifAbsent: [^ smalltalk error:
X				'missing class name in Class directive'])
X					asSymbol.
X		globalNames at: name put: ( Class new;
X			name: name;
X			superClass: (globalNames at: (
X				commandLine at: 3
X				ifAbsent: [ ^ smalltalk error:
X					'missing superclass name'])
X					asSymbol
X				ifAbsent: [ ^ smalltalk error:
X					'unknown class']);
X			variables: (commandLine copyFrom: 4 to:
X					commandLine size ) )
X|
X	fileInMethod: commandLine
X		(commandLine size ~= 2)
X			ifTrue: [ ^ smalltalk error: 
X				'invalid Method command line '].
X			(globalNames at: (commandLine at: 2) asSymbol
X				ifAbsent: [ ^ smalltalk error:
X					'unknown class in Method header'])
X				fileInMethod: self
X|
X	getString
X		^ (pointer notNil)
X			ifTrue: [<125 pointer>]
X|
X	getPrompt: aString
X		stdout printNoReturn: aString.
X		^ self getString
X|
X	inquire: aString	| response |
X		response <- self getPrompt: aString.
X		response isNil 
X			ifTrue: [ ^ false ].
X		^ 'yY' includes: (response at: 1 ifAbsent: [])
X|
X	print: aString
X		(pointer notNil)
X			ifTrue: [<129 pointer aString>]
X			ifFalse: [smalltalk error: 'file not open']
X|
X	printNoReturn: aString
X		(pointer notNil)
X			ifTrue: [<128 pointer aString>]
X			ifFalse: [smalltalk error: 'file not open']
X|
X	readUntil: conditionBlock doing: actionBlock	| line |
X		[ line <- self getString. line notNil]
X			whileTrue: [ (conditionBlock value: line)
X					ifTrue: [ ^ line ].
X					actionBlock value: line ].
X		^ nil
X|
X	saveImage
X		(pointer notNil)
X			ifTrue: [<127 pointer>]
X			ifFalse: [smalltalk error: 'file not open']
X]
XClass Method
X	executeWith: arguments
X		^ ( Context new ; method: self ; 
X			temporaries: ( Array new: temporarySize) ;
X			arguments: arguments )
X		   executeFrom: 0 creator: nil
X]
XClass Class
X	addMethod
X		self doEdit: ''
X|
X	addSubClass		| name |
X		name <- (stdin getPrompt: 'Class Name? ') asSymbol.
X		globalNames at: name put: 
X			( Class new; name: name ; superClass: self ;
X				readInstanceVariables; readMethods )
X|
X	addMethodText: text		| theMethod |
X		theMethod <- Method new; text: text.
X		(theMethod compileWithClass: self)
X			ifTrue: [ methods at: theMethod name put: theMethod.
X				  smalltalk flushMessageCache.
X				  ^ true ].
X		^ false
X|
X	doEdit: startingText		| text |
X		text <- startingText.
X		[ text <- text edit.
X		  (self addMethodText: text)
X			ifTrue: [ false ]
X			ifFalse: [ stdin inquire: 'edit again (yn) ? ' ]
X				] whileTrue
X|
X	display
X		('Class name: ', name asString)  print.
X		(superClass notNil)
X			ifTrue: [ ('Superclass: ', superClass ) print ].
X		'Instance Variables:' print.
X		variables isNil
X			ifTrue: [ 'no instance variables ' print ]
X			ifFalse: [ variables display ].
X		'Subclasses: ' print.
X		self subClasses display
X|
X	editMethod: name
X		self doEdit: ( methods at: name
X			ifAbsent: [ 'no such method ' print. ^ nil ] ) text
X|
X	fileInMethod: file	| text line |
X		text <- ''.
X		line <- file readUntil: [:x | '|[' includes: 
X					(x at: 1 ifAbsent: [] ) ]
X				doing: [:x | text <- text , x].
X		self addMethodText: text.
X		^ line
X|
X	fileOut: file
X		file printNoReturn: 'Class ', name asString.
X		file printNoReturn: ' ', superClass name asString.
X		variables do: [:x | file printNoReturn: ' ', x ].
X		file print: ''.
X		methods do: [:x | self fileOutMethod: x name to: file ]
X|
X	fileOutMethod: method to: file
X		file print: 'Method ', name asString.
X		file print: (methods at: method
X			ifAbsent: [^ smalltalk error:
X				'no such method' ]) text.
X		file print: '|'
X|
X	readInstanceVariables
X		self variables:
X			((stdin getPrompt: 'Instance Variables? ')
X			words: [:x | x isAlphabetic ])
X|
X	readMethods
X		[ stdin inquire: 'Add a method (yn) ? ' ]
X			whileTrue: [ self addMethod ]
X|
X	viewMethod: name
X		(methods at: name
X			ifAbsent: [ 'no such method ' print. ^ nil ]) text print
X]
XClass Smalltalk
X	error: aString
X		stderr print: 'Error: ',aString.
X		errorRecoveryBlock value
X|
X	openFiles
X		stdin name: 'stdin'.
X		stdin open: 'r'.
X		stdout name: 'stdout'.
X		stdout open: 'w'.
X		stderr name: 'stderr'.
X		stderr open: 'w'.
X|
X	commandLoop	| string |
X		self openFiles.
X		[ string <- stdin getPrompt: '>	'. string notNil ]
X			whileTrue: [ (string size strictlyPositive)
X					ifTrue: [ self doIt: string ] ]
X|
X	doIt: aString		| method |
X		errorRecoveryBlock <- [ ^ nil ].
X		method <- Method new.
X		method text: ( 'proceed ', aString ).
X		(method compileWithClass: Object)
X			ifTrue: [ method executeWith: #( 1 ) ]
X|
X	saveImage		| name |
X		name <- stdin getPrompt: 'type image name: '.
X		File new;
X			name: name;
X			open: 'w';
X			saveImage;
X			close.
X		('image ', name, ' created') print
X]
XClass String
X	edit	| file text |
X		file <- File new; 
X			scratchFile;
X			open: 'w';
X			print: self;
X			close.
X		(editor, ' ', file name) unixCommand.
X		file open: 'r'.
X		text <- file asString.
X		file close; delete.
X		^ text
X|
X	print
X		^ stdout print: self
X|
X	unixCommand
X		^ <88 self>
X]
/
echo 'Part 04 of small.v2 complete.'
exit
-- 
For comp.sources.unix stuff, mail to sources at uunet.uu.net.



More information about the Comp.sources.unix mailing list