Please go to the svn repository for the code: http://www.forth-ev.de/repos/simpleOOP/
And here is documentation as a PDF file: http://www.forth-ev.de/repos/simpleOOP/doc/simpleOOP.pdf
File OOP.FS holds the definitions for „simpleOOP“, which loads on top of gforth 0.62. It makes maximum use of the existing Forth mechanisms to realize OO and therefore, it is very concise - 415 lines of code realize all mechanisms, which can be expected from an OO package to the best of my knowledge, namely: Polymorphism, late binding, single inheritance and Proxies.
At present the code is dependent on gforth 0.62, because of the way the interpreter and compiler has to be redefined. It could be easily ported to gforth 0.71 as well. I believe that the concepts used can be expressed in Standard Forth, but I am not an expert in it and therefore, I would need to collaborate with one if needed.
File TEST.FS defines classes CELL, BUFFER, and STRING and a couple of methods as a proof of concept. These definitions are followed by some test code, which predominantely test proxy handling and polymorphism (@ in String). File DEMO.FS is another test case file that concentrates on polymorphism and late binding. This work is based on the ideas put forward by Manfred Mahlow for the first time. His concepts revolved around the so called „prelude concept“, which would simplify class context setting without the need to make object defining words immediate and state smart. But using the prelude concept requires a modification of forth's headers, which requires system dependent modifications and recompilation of the Forth system. As an alternativ, I modified the outer interpreter and compiler of gforth to handle context switching between the Forth and the Class context. Therefore, all words (e.g. OBJECT), which modify the context, need to be immediate. Methods are not immediate and they compile to a simple colon definition call.
Initially I implemented the OO package in the cross-compiler for uCore - without late binding nor polymorphism. But Classes, Objects, Attributes, Proxys, and Arrays and their inheritable methods were already there. Thanks to Andrew Haley's insistence I started to think about an efficient late-binding mechanism, and the solution I found is embarrassingly simple and efficient. Its runtime overhead is one additional Forth branch compared to static :-definitions. And Bernd Paysan argued that I couldn't possibly do without vTables for proper polymorphism. As it turns out, Bernd was both right and wrong: simpleOOP does not have classical vTables; instead, the wordlist associated with each class constitutes the vTable of that class. These „virtual“ vTables are indexed by pattern-matching (searching for a name), the table entries are directly executable (which is unusual), and the dynamic binding element is a re-writeable branch. To preserve polymorphism on inheritance, all words of the superclass will be physically copied into the subclass's wordlist upon creation, much like you make a copy of the vTable.
Instead of creating new words for OOP handling, I tried to make existing words behave differently depending on whether they are used in a Forth or in a Class context. Therefore, there is e.g. no special defining word for methods. : just behaves differently, if we are compiling into a class wordlist. This helps to minimize the number of new words needed for simpleOOP and the resulting code looks very „forthish“.
In a class compilation context, „: <name>“ produces a :-definition, which starts with a branch to its code body. Therefore, the semantics of <name> depends on the (re-writeable) branch destination. Later on, when „: <name>“ is redefined in order to e.g. instrument a method, no new header is created, but the branch destination of its first version is re-written. This is an efficient late binding mechanism.
Polymorphic methods have to be declared explicitly using „Polymorphic <name>“. This allocates space for an XT in each instantiated object and its „default“ behaviour can be specified using „: <name>“ later on. In addition, an object specific behaviour can be assigned using bind. Polymorphic methods can only be created while a class is not yet sealed. It will be sealed automatically as soon as a) an object of that class is created, b) the class is embedded in another class as an attribute, or c) when a subclass is created.
Klaus Schleisiek