Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!wuarchive!mit-eddie!bloom-beacon!eru!luth!sunic!liuida!mip From: mip@IDA.LiU.SE (Mikael Patel) Newsgroups: comp.lang.forth Subject: Object oriented programming extension for forth. Message-ID: <1990Aug21.170600.26447@ida.liu.se> Date: 21 Aug 90 17:06:00 GMT Sender: news@ida.liu.se (News Subsystem) Organization: CIS Dept, Univ of Linkoping, Sweden Lines: 601 Lately a number of postings have been on the subject of object oriented programming extensions for forth. I have been working on an extension (actually three models) and wish to distribute and share the definition of one for discussion. In this posting I will only present the class-instance model (because of space). The other models, relations and prototypes, are more general and actually used to implement the presented model. The source code for all these models are available in the coming release of TILE forth (version 3.33, release 2.1). The below included files are the manual pages for the OOP extension and the test/example file. The syntax and notation is as close to Smalltalk-80 as possible. Messages are defined and used as functions (a normal colon definition). A message always requires an instance on top of stack. It is with the class information from the instance late binding is performed. A class is defined with internal fields (much like a structure) and methods for messages. The definition of a class also requires a super class (which may be nil for a root class). To call the super class realization of a message in a method the message prefix word "super" is used. The overall syntax (in EBNF) is: subclass { } { method ( ... self -- ...) ... ... ; } subclass.end An instance is created in the following style: instance ( -- object) or new-instance ( -- object) As shown the instance can be "bound" or "free". The message "initiate" is always passed to the newly created instance to perform initialization Some of the latest postings have suggested using a prefix message-passing operator. I have here viewed the message as a "normal" forth word. It will at run-time locate the appropriate method using information from the class structure (or internal information) and apply the method. A typical example of this style of message passing is: message initiate ( ... self -- ) message moveTo ( x y self -- ) ... 10 10 Point instance aPoint ... -10 10 aPoint moveTo The style follows "normal" forth and there is, in principle, no difference between a colon definition and a message on the "surface". The below example file shows an object root class, a point class, and the classical account example. Also a proxy class is shown in the form of a traceable class. The error message "doesNotUnderstand" is here defined as a method which passes on the message to the actual object. This way of redefining the error message may also be used to implement multiple inheritance. I believe that we should look for the definition of a common object oriented extension for forth and then work on the real problem: A library with reusable classes. The below definitions are only a small step on this road. The next step is yours. Mikael R.K. Patel Researcher and Lecturer Computer Aided Design Laboratory (CADLAB) Department of Computer and Information Science Linkoping University, S-581 83 LINKOPING, SWEDEN Phone: +46 13281821 Telex: 8155076 LIUIDA S Telefax: +46 13142231 Internet: mip@ida.liu.se UUCP: {uunet,mcsun,...}!liuida!mip Bitnet: MIP@SELIUIDA SUNET: LIUIDA::MIP - - - - - - - - File: object.tst - - - - - - - - - - - - - - - - - .( Loading Objects test...) cr #include objects.f83 objects string forth definitions \ The set of messages needed for this example message initiate ( self -- ) message isKindOf ( class self -- boolean) message isMemberOf ( class self -- boolean) message respondsTo ( message self -- boolean) message instanceSize ( self -- num) message copy ( self -- object) message shallowCopy ( self -- object) message deepCopy ( self -- object) message doesNotUnderstand ( message self -- ) message subclassResponsibility ( self -- ) message shouldNotImplement ( self -- ) message perform ( message self -- ) message basicWrite ( self -- ) message write ( self -- ) message read ( self -- ) message where ( self -- x y) message position ( x y self -- ) message balance ( self -- x) message owner ( self -- x) message deposit ( x self -- ) message withdraw ( x self -- ) message balance ( self -- x) \ A class hierarchy with Object, Point, and Account nil subclass Object ( -- ) method initiate ( self -- ) basicWrite ." initiated" cr ; method isKindOf ( class self -- bool) class begin 2dup = if 2drop true exit then superclass dup 0= until 2drop false ; method isMemberOf ( class self -- bool) class = ; method respondsTo ( message self -- bool) class canUnderstand ; method instanceSize ( self -- num) class basicInstanceSize ; method copy ( self -- object) shallowCopy ; method shallowCopy ( self -- object) here swap 2dup instanceSize dup allot cmove ; method deepCopy ( self -- object) copy ; method doesNotUnderstand ( message self -- ) basicWrite ." does not understand: " .message cr abort ; method subclassResponsibility ( message self -- ) basicWrite ." subclass should have overridden: " .message cr abort ; method shouldNotImplement ( message self -- ) basicWrite ." should not implement: " .message cr abort ; method perform ( message self -- ) tuck class send ; method write ( self -- ) basicWrite ; method basicWrite ( self -- ) dup .class ." #" . ; subclass.end Object subclass Point ( x y -- ) long +x ( self -- addr) long +y ( self -- addr) method initiate ( x y self -- ) dup >r super initiate r> position ; method position ( x y self -- ) tuck +y ! +x ! ; method where ( self -- x y) dup +x @ swap +y @ ; method write ( self -- ) dup super write where ." x: " swap . ." :y " . ; subclass.end Object subclass Account ( x -- ) long +owner ( self -- addr) long +balance ( self -- addr) method initiate ( x self -- ) dup >r super initiate r> 0 over +balance ! +owner ! ; method balance ( self -- x) +balance @ ; method owner ( self -- x) +owner @ ; method deposit ( x self -- ) +balance +! ; method withdraw ( money self -- ) swap negate swap +balance +! ; method write ( self -- ) dup super write ." owner: " dup owner $print space ." balance: " balance . ; subclass.end : DEBUG ; #ifdef DEBUG message traceOn ( object -- ) message traceOff ( objec -- ) nil subclass TraceableObject ptr +real-object ( object -- addr) long +trace ( object -- addr) method initiate ( class self -- ) >r new-instance r@ +real-object ! r> traceOff ; method doesNotUnderstand ( message self -- ) dup +trace @ if 2dup +real-object @ basicWrite ." called with the message: " .message cr then +real-object @ tuck class send ; method traceOff ( object -- ) false swap +trace ! ; method traceOn ( object -- ) true swap +trace ! ; subclass.end #else : TraceableObject ; #then Object instance anObject anObject write cr anObject read cr 10 10 Point instance aPoint aPoint write cr -10 -10 aPoint position aPoint write cr aPoint read cr 10 10 Point TraceableObject instance aTracedPoint aTracedPoint traceOn aTracedPoint write cr -10 -10 aTracedPoint position aTracedPoint write cr aTracedPoint read cr " Mikael" Account instance anAccount anAccount write cr 100 anAccount deposit anAccount write cr 98 anAccount withdraw anAccount write cr cr forth only - - - - - - - - File: object.3 - - - - - - - - - - - - - - - - - OBJECTS(3X) MISCELLANEOUS LIBRARY FUNCTIONS OBJECTS(3X) NAME objects - object oriented programming extension SYNOPSIS #include objects.f83 objects DESCRIPTION This library realizes the popular class-instance model of object oriented programming. The "world" is divided into classes and instances. The classes hold all common informa- tion for the instances. This information is the instance variable fields and the messages an instance can answer. The answer of a message is called a method. A method may call the super class implementation of a message with the prefix word "super". This creates a static binding of the message to the super class. A messages require the object as the top most parameter stack element and will locate an appropriate method at run-time (late-binding). : .class ( object -- ) Displays the name of the class of an object on the current output stream. : .message ( message -- ) Displays the name of the message on the current output stream. May be used by the error message "doesNotUnder- stand" which receives a message as parameter. : align ( -- ) Instance variable layout control word. Used to align the next field to an even byte boundary within the definition section of a class. : basicInstanceSize ( class -- num) Returns the instance size in bytes. subclass.field byte ( -- ) Used within the instance variable section of a class definition to create a byte field name. byte ( object -- addr) The field will require an object and will return the address of the byte within the object. : bytes ( size -- ) Used within the instance variable section of a class definition to create a byte vector field name. bytes ( object -- addr) The field will require an object and will return the address of the byte vector within the object. : canUnderstand ( message class -- bool) Returns "true" if the class can understand the message (there exists a method) else "false". : class ( object -- addr) Returns the class address for the object. The class is implemented as a prototype. forward doesNotUnderstand ( message object -- ) Forward declared error message. Sent to an object when the message lookup mechanism fails. May be used to implement "proxy" objects. subclass.field enum ( -- ) Used within the instance variable section of a class definition to create a enumerate field name. enum ( object -- addr) The field will require an object and will return the address of the enumerate within the object. forward initiate ( ... object -- ) This word is called by "new-instance" and "instance" and should be a message used to initiate the newly created object. : instance ( class -- ) Used in the following form: instance ( -- object) to create a named instance of a class. The message "initiate" is send to the newly created instance. This message must be answered. subclass.field long ( -- ) Used within the instance variable section of a class definition to create a long integer field name. long ( object -- addr) The field will require an object and will return the address of the long integer within the object. : message ( -- ) Used in the following form: message ( ... object -- ... ) to create a message. The message may be passed to an object as a normal function call. The top most parame- ter is always required to be an object. A method for the message is located at run-time giving late binding, and polymorphism. : method ( -- ) Used in the following form within the definition sec- tion of a class: method ( ... object -- ...) ... ; to define a method for the message. The method will receive the object, self, as the first parameter. To pass the message to the superclass the message prefix word "super" may be used. This is common practice in object oriented programming. The method definition should not contain the words "recurse", "tail-recurse", and "exception>" as these require an entry binding. : new-instance ( class -- object) Used in the following form: new-instance ( -- object) to create an instance of a class. The message "ini- tiate" is sent to the newly created instance. This mes- sage must be answered or the error message "doesNo- tUnderstand" is sent to the instance. vocabulary objects ( -- ) The object oriented extension vocabulary. Include to the vocabulary search set, "context", to allow program- ming in the object oriented programming paradigm. subclass.field ptr ( -- ) Used within the instance variable section of a class definition to create a pointer field name. ptr ( object -- addr) The field will require an object and will return the address of the pointer field within the object. : send ( object message class -- object) Primitive message sending function. Used by the normal message-passing, "message", mechanism to locate and apply a method. : subclass ( superclass -- ) Used in the following form: subclass { } { } subclass.end to initiate the definition of a class. This definition should contain two major sections; the instance vari- able fields, and the instance methods. All field names will become local to the class. The words "byte", "bytes", "enum", "word", "long" and "ptr" should be used to create instance variable field names. These will require an object pointer and will return the address of the corresponding field. The field should then be manipulated with the memory access functions. The word "align" may be used before a field name defin- ition to align it to an even byte boundary. The method definition section may contain implementations of mes- sages. A method may be viewed as a normal colon definition with the extension that many definitions, methods, for the same name, message, may exist simul- taneous and the selection is performed at run-time (late binding). The method should always receive the object, self, as the top most parameter. The message prefix word "super" may be used to call the super class implementation of a method. This binding is static. The word "this-class" may be used to access the current class. The method may not use the words "recurse", "tail-recurse", and "exceptions>" as these require an entry binding. : subclass.end ( -- ) Used in the following form: subclass { } { } subclass.end to finish the definition of a class. Additional methods and fields should not be defined after this word. : super ( -- ) immediate compilation Used in the following form within a method definition: super to call a method for a message in the superclass. This word should only be used inside of method definitions. : superclass ( class1 -- class2) Returns the address super class of the class. The value is "nil" for a root class. : this-class ( -- class) immediate Returns the address of the current definitions class. Will return "nil" outside of a class definition. subclass.field word ( -- ) Used within the instance variable definition section of a class to create a word integer field name. word ( object -- addr) The field will require an object and will return the address of the word integer within the object. INTERNALS Private definitions in the objects vocabulary; slot instance-size: ( class -- num) private Slot for a class (prototype) containing the number of byte for an instance of this class. slot instance-variables: ( class -- entry) private Slot for a class (prototype) containing a pointer to the last defined instance variable field entry. The list of variables is terminated by the class itself. : subclass.field ( size -- ) private Use to create instance variable field type names. These field types should only be used within a class defini- tion. variable the-class ( -- addr) private Used within a class definition section to access the address of the current definition class. Outside of the definition section the variable will have the value "nil". SEE ALSO tile(1), forth(3X), prototypes(3X). NOTE The function list is sorted in ASCII order. The type and mode of the entries are indicated together with their param- eter stack effect. WARNING This library contains forward declared words which should be supplied. Check that your definitions vocabulary comes before the "objects" vocabulary when defining these words. COPYING Copyright (C) 1990 Mikael R.K. Patel Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this per- mission notice are preserved on all copies. Permission is granted to copy and distribute modified ver- sions of this manual under the conditions for verbatim copy- ing, provided also that the section entitled "GNU General Public License" is included exactly as in the original, and provided that the entire resulting derived work is distri- buted under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above condi- tions for modified versions, except that the section enti- tled "GNU General Public License" may be included in a translation approved by the author instead of in the origi- nal English. AUTHOR Mikael R.K. Patel Computer Aided Design Laboratory (CADLAB) Department of Computer and Information Science Linkoping University S-581 83 LINKOPING SWEDEN Email: mip@ida.liu.se