A | B | C | D | |
---|---|---|---|---|
1 | BOB'S projects with ESP32forth | |||
2 | WORD | STACK | COMMENTS | |
3 | ||||
4 | MINI-OOF by Bernd Paysan 1998 adapted by Bob Edwards 24th July 2021 | Mini Object Oriented Forth is a powerful tool to help you turn esp32forth into an application specific language, reducing coding time and making for easier maintenance. However, use it only when a more simple and elegant solution eludes you. Always write the simplest code that works for the here and now | ||
5 | ||||
6 | DEFINED? *MINI-OOF* [IF] forget *MINI-OOF* [THEN] : *MINI-OOF* ; | If MINI-OOF was previously compiled, remove it before recompiling | ||
7 | ||||
8 | \ Words missing from the esp32forth system | |||
9 | ||||
10 | : NOOP ; | do nothing - a place holder for the vector table overwritten later by a method xt | ||
11 | : /STRING DUP >R - SWAP R> + SWAP ; | ( addr1 cnt1 n -- addr2 cnt2 ) | remove n chars from the front of a counted string or memory block | |
12 | ||||
13 | \ The object oriented extensions | |||
14 | ||||
15 | : METHOD CREATE OVER , SWAP CELL+ SWAP DOES> @ OVER @ + @ EXECUTE ; | ( m v -- m' v ) ( .. o -- ... ) | During method declaration, the number of methods and instance variables is on the stack (in address units). method creates one method and increments the method number. To execute a method, it takes the object, fetches the vector table pointer, adds the offset, and executes the execution token stored there. Each method takes the object it is invoked from as top of stack parameter. The method itself should consume that object. | |
16 | : VAR CREATE OVER , + DOES> @ + ; | ( m v size -- ) ( o -- addr ) | Same as above, a word is created with the current offset. Instance variables can have different sizes (cells, floats, doubles, chars), so all we do is take the size and add it to the offset. If your machine has alignment restrictions, put the proper aligned or faligned before the variable, it will adjust the variable offset. That's why it is on the top of stack. | |
17 | : CLASS DUP 2@ SWAP ; | ( class -- class methods vars ) | A CLASS definition (recipe for cloning OBJECTS that can store data and run class specific code) is started with this word | |
18 | : END-CLASS CREATE HERE >R , DUP , 2 CELLS ?DO ['] NOOP , 1 CELLS +LOOP CELL+ DUP CELL+ R> ROT @ 2 CELLS /STRING CMOVE ; | ( CLASS totalMETHODspace totalVARspace "name" -- ) | Now, for inheritance, the vector table of the parent object has to be copied, when a new, derived class is declared. This gives all the methods of the parent class, which can be overridden (different methods substituted), if required First we create the vector table, initialized with noops. Then the words starting CELL+ DUP CELL+ ... is the inheritance mechanism, it copies the execution tokens from the parent vector table | |
19 | : DEFINES ' >BODY @ + ! ; | ( xt class -- ) | This allows to define new METHODS - actions that OBJECTS made from the CLASS can do | |
20 | : NEW HERE OVER @ ALLOT SWAP OVER ! ; | ( class -- o ) | This word is used to create new OBJECTS ( named clones that can be run ) of the required CLASS | |
21 | : :: ' >BODY @ + @ , ; | ( class "name" -- ) | And sometimes derived classes want to access the method of the parent object. There are two ways to achieve this with this OOF: first, you could use named words, and second, you could look up the vtable of the parent object. | |
22 | CREATE OBJECT 1 cells , 2 cells , | ( -- ) | We need a starting point (the empty object from which all other classes arise) | |
23 | ||||
24 | ||||
25 | \ Example MINI-OOF code | |||
26 | ||||
27 | object class cell var teeth# cell var height method speak method greet method walk method add. end-class pet | ( -- ) | CREATE ... DOES> are very useful. They allow us to clone our own arrays, lists, queues, stacks and all manner of simple objects - as many as we want. HOWEVER, each object only does one thing - e.g. when an array word is executed, the start address is returned on the stack. A CLASS is an extension of a CREATE ... DOES> word. You can see there are two 'var' declared to store data (each var can have more space e.g. 3 cells var myarray). Also declared are a number of METHODS or things that this class can do. What the METHODS actually do is defined next This is just a recipe for creating one or more OBJECTS of type 'pet'. It isn't directly executed, so don't try doing that! | |
28 | :noname ." pet speaks" drop ; pet defines speak :noname ." pet greets" drop ; pet defines greet :noname ." pet walks" drop ; pet defines walk :noname drop + ." n1 + n2 = " . ; pet defines add. | ( n1 n2 -- ) | OK - these words define what each method actually does. Notice that method 'add.' takes in two parameters from the stack Why are we dropping something in each method? Well - when a method executes, a reference to the particular pet we wish to operate on is top of the data stack. By convention we're supposed to remove that from the stack during execution of a method We've completely defined CLASS pet. Remember we still can't run anything yet | |
29 | pet class method happy end-class cat | ( -- ) | Suppose we have a variation on pet that we wish to make: Class 'cat' does slightly more than a pet can do. We define the extra method 'happy' | |
30 | :noname ." cat purrs" drop ; cat defines happy | ( -- ) | And here we define what 'happy' will actually do in a cat object Again, just another recipe - we still can't run anything | |
31 | :noname ." cat says meow" drop ; cat defines speak :noname ." cat raises tail" drop ; cat defines greet | ( -- ) | Here we've decided that the default methods supplied by 'pet' don't suit cat, so we've substituted or overrided those methods with these | |
32 | pet class end-class dog | ( -- ) | Here's another class of type pet called dog | |
33 | :noname ." dog says wuff" drop ; dog defines speak :noname ." dog wags tail" drop ; dog defines greet | ( -- ) | And again, dogs do something different than pets for these methods NB you don't have to override all methods - that's up to you to suit the application | |
34 | cat new constant tibby dog new constant fido | ( -- ) | OK, at last we've created two actual pets as OBJECTS or INSTANCES of classes cat and dog - we can make these execute their methods, read and write their data and so on | |
35 | 20 tibby teeth# ! 30 fido teeth# ! 50 tibby height ! 75 fido height ! | ( -- ) | Here we are setting vars in our two objects Notice the syntax is: <data> <objectname> <varname> ! varname adds an offset to objectname to form the address to store our data | |
36 | tibby teeth# @ . cr fido height @ . cr | ( -- ) | And this is the opposite, we're reading data from the two objects. The syntax is <objectname> <varname> @ | |
37 | tibby greet fido speak tibby speak | ( -- ) | Here, we're getting the two pets to do stuff - it'll just display the message defined in each method in our simple demo Notice how the word 'speak' does different things dependent on which pet is referenced. Reuse of words cuts the number that the programmer has to make up or remember. No more catspeak and dogspeak type of words needed | |
38 | 34 56 fido add. | ( n1 n2 -- ) | Our pets are pretty smart - here's fido adding two numbers, that we've place on the stack ready for that method. It could also leave stuff on the stack if that were required | |
39 | tibby walk | ( -- ) | Notice that cat ( and dog ) have INHERITED all of pet's methods. 'walk' wasn't overidden, so both dog and cat know how to walk | |
40 | ||||
41 | \ The above packs quite a lot of punch for very little code space | |||
42 | \ and may make all the difference to an otherwise awkward-to-code job | |||
43 | \ Bernd Paysans Mini-OOF pages | |||
44 | \ An extension to Mini-OOF by Gerry Jackson might also be interesting |