There are two sets of documentation for the ClassVFX system. There is a chapter in the main VFX Forth manual, and there is a full PDF manual in the Manual subdirectory of Lib\OOP\ClassVFX\.
The source code is in the directory Lib\OOP\ClassVFX. The file MakeClassVfx.bld is compiled to produce the production version of the code. TestClassVfx.fth contains test code.
ClassVFX was developed over a number of years in collaboration with Construction Computer Software of Cape Town, South Africa. We gratefully acknowledge their collaboration and permission to release it. ClassVFX is heavily used in their construction industry planning software, which is one of the largest Forth applications ever written. Modifications to ClassVFX will only be released after the agreement of CCS.
ClassVFX is a halfway house between a full object oriented system and an intelligent structures system. Types, or classes, can be defined with single inheritance. Method names have to be predefined using
OPERATOR: <method-name>
Field, or data member, names are private, but are accessible
using a dot notation. There are no equivalents of SUPER
and SELF
. There is no late binding.
In this documentation types and classes are synonymous. Objects are instances of a type. Objects have a default action if no method is specified. Usually the default action is to fetch the contents of the object, but in a few cases the default action is to return an address.
Types/Classes can have both class and instance methods. The default method for a type is to create an instance. If a type is used inside a colon definition a local variable version is created and destroyed at run time.
Operators, or methods, must be declared as above before use.
TYPE:
definitions may be used in four ways:
Point
is a type (class).Point.x or x
Point: MyPoint
LOCALS| ... |
mechanism or the MPE { ... } mechanism
has been used the use of Point: foo
inside a colon
definition will simply add FOO
to the existing local
frame.TYPE:
definition.
operator: <method1>
operator: <method2>
operator: <method3>
type: line:
point: start
point: end
:m <method1> ... ;m
:m <method2> ... ;m
mruns <method3> <some-word>
:m <xxx> a b c d ;m structure-method
end-type
Line: MyLine
1 2 to Myline.start
5 to Myline.end.y
At runtime, the method operates on the address of the data. Because of
this, a method which requires the address of the instance structure has
to be marked by the word STRUCTURE-METHOD
which causes the compiler to
generate the address of the instance structure, not the type structure.
ClassVFX allows both CLASS
and INST
ance methods
to be defined for a type. INST
ance methods, the default,
operate on the address of the data item. CLASS
methods
operate on the address of the type data structure. As described
above, STRUCTURE-METHOD
s operate on the instance data
structure.
Single inheritance can be defined using SUPERCLASS <type>
or INHERITS <type>
before any field or method is defined.
TYPE: <type> SUPERCLASS <supertype>
...
END-TYPE
At run-time, methods are provided with the address of the
required data. CLASS/TYPE methods receive the address of the
TYPE/CLASS data structure, INSTance methods receive the address
of the data item. INSTance methods that require the address
of the instance data structure must be marked by
STRUCTURE-METHOD
. Methods may be defined as nameless
words:
:M <method-name> ... ;M
or as the action of a method:
MRUNS <method-name> <action-name>
The code below is taken from the definition of the default type.
class \ define methods for the type
:m default make-inst ;m
:m sizeof type-size @ ?complit ;m
:m addr ?complit ;m
inst \ define methods for the instance
mruns default noop
:m sizeof type-size @ ?complit ;m structure-method
mruns addr noop
:m offsetof off-start @ ?complit ;m structure-method
:m +offsetof off-start @ ?complit+ ;m structure-method
To use the nested field system, the Forth system has been
modified to accept compound names in which the elements of
the structure are separated by the `.' character. This
feature is enabled and disabled by the words +STRUCTURES
and -STRUCTURES
.
char: byte - 8 bit variable
word: word - 16 bit variable
int: long - 32 bit variable and synonyms
dword:
long:
ptr:
xlong: longlong - 64 bit variable
bytes: byte array: size specified by n BYTES BYTES:
cstring: counted string: size specified by BYTES before CSTRING:
zstring: zero term. string: size specified by BYTES before ZSTRING:
field: byte array, only ADDR operator, size specified by BYTES
Note that not all predefined types support all methods.
0 operator default usually a fetch operation
1 operator -> store operator
1 operator to "
2 operator addr address operator
3 operator inc increment by one
4 operator dec decrement by one
5 operator add n add to
6 operator zero set to 0
7 operator sub subtract from
8 operator sizeof size
9 operator set set to -1
10 operator offsetof offset in object
11 operator +offsetof add offset in object
12 OPERATOR FETCH get contents
13 OPERATOR ADDR\CNT address under count
14 OPERATOR TWIST change endian of the data type
15 OPERATOR CONSTRUCT build an instance of this type
15 OPERATOR MAKE build an instance of this type
op# ADDR OPERATOR ADDROF
OPERATOR: <=> type_addr_y <=> <type_x> --- set typedef_x = typedef_y
op# <=> OPERATOR <copy>
OPERATOR: <blank> blank object for object size
OPERATOR: <erase> fill obj with null for object size
OPERATOR: <COUNT>
OPERATOR: <make>
OPERATOR: <destroy>
OPERATOR: <INIT>
OPERATOR: <fetch>
TYPE: POINT: \ --
\ Defines a type called POINT: with the following fields )
PROVIDER: NOOP \ defines the address provider, defaults to NOOP
0 OFFSET: \ defines the initial offset, defaults to 0
INT: Y
INT: X
10 BYTES FIELD: FOO
\ fetch operation
:m default
2@
;m
mruns to 2!
...
END-TYPE
TYPE:
definitions, fields, objects and so on all use a
common data structure that is generated by the defining words.
These structures are associated with a word (the address
provider) that can provide the starting address of the
structure implementation. By supplying the cfa of NOOP
,
no address is provided, and so the structure is purely a
template. For templates, address provider = 0 or NOOP
,
an offset may also be defined. NOOP
and 0 are the
default address provider and offset of templates. )
A similar structure is used for instances of a TYPE:
.
These are created by the word MAKE-INST
.
The following structure is created by TYPE:
header standard PFW layout
0 jmp do_type 5 bytes
1 cfa of address provider 4 bytes
2 initial offset 4 bytes 0 for class
3 link to last field defined 4 bytes
4 type size - final offset 4 bytes
5 Magic number 4 bytes
6 anchor of instance method chain 4 bytes
7 anchor of type method chain 4 bytes
8 link to previous type defined 4 bytes
9 private wordlist ? bytes
The following structure is created by MAKE-INST
header standard PFW layout
0 jmp do_inst 5 bytes
1 cfa of address provider 4 bytes
2 offset from start of type 4 bytes
3 link to last instance of type 4 bytes
4 size of instance data 4 bytes
5 0 4 bytes
6 pointer to TYPE/CLASS 4 bytes
7 data if static
When an instance is defined inside a colon definition, an uninitialised local variable/array is built. Several instances can be built. Normally the size of all local variables is rounded up to a cell boundary by the compiler
: method, \ struct "<method>" -- struct ^xt
Given a TYPE structure, lay an entry in one of the method chains.
: :M \ struct "<method>" -- struct ; :M <operator> <actions ...> ;M
defines the start of a method. The method/operator name must
follow.
: ;M \ struct -- struct ; SFP012
marks the end of a method definition.
: MRUNS \ struct "<method>" "<word>" -- struct ; MRUNS <operator> <word>
Defines a method which runs a previously defined word.
: CREATE-INST \ "<name>" -- ; -- addr
From VFX Forth v4.4, this is a synonym for CREATE
.
When compiling on previous VFX versions instances needed to
be immediate.
: make-inst \ class -- ; i*x -- j*y ; build instance of type
Builds an instance of a TYPE:
. This word has serious
carnal knowledge of the internal workings of VFX Forth.
Don't call us for help!
create type-template \ -- addr
The type chain from which others are derived.
CREATE ptr-template \ -- addr
The ptr chain from which others are derived.
: type:-runtime \ type-struct --
The run-time action of children of TYPE:
.
: CURR-TYPE-SIZE \ -- u
Use between TYPE: <name> and END-TYPE to return the current
size of the type.
: TypeChildComp, \ xt --
Compile a child of TYPE:
.
: type: \ -- struct ; --
Start a new TYPE:
definition.
: PTR: \ -- struct ; --
Make a new structure defining word.
: end-type \ struct --
Finish off a TYPE:
definition
: EXTEND-TYPE \ "<type>" -- struct ; EXTEND-TYPE <type> ... END-TYPE
Extend the given TYPE:
definition.
: SUPERCLASS \ struct "<type>" -- struct
Use this inside a TYPE:
definition before defining any
data or methods. The current type will inherit the data and
methods of the superclass.
: INHERITS \ struct "type" -- struct
A synonym for SUPERCLASS
.
: provider: \ struct "name" -- struct ; <name> is address provider
Sets a different address provider.
: with: \ -- ; WITH: <some-provider> LINE: <myline>
Used before declaring an instance to override the default address
provider.
: SKIPPED \ struct size -- struct
Increase overall size of struct by size. SKIPPED
can
be used to jump over items from a previous instance.
: OFFSET: \ struct offset -- struct
Define the offset of a TYPE:
as starting at a value
other than zero. Must be used before any data is defined.
: TypeCast: \ -- ; TYPECAST: <inst> <type>
Forces a previously defined instance to be a pointer to a type/class.
SYNONYM PointsTo: TypeCast: \ -- ; synonym for TYPECAST:
Forces a previously defined instance to be a pointer to a type/class.
: type-self \ -- type
Used in TYPE: <name> ... END-TYPE to refer to the type/class
being defined.
: EXECUTE-MEMBER-METHOD \ struct-inst member-inst methodid ---
Attempt to execute method for inst. Return true if successful.
: EXECUTE-PTR-MEMBER-METHOD \ member-inst methodid ---
Attempt to execute method for inst. Return true if successful.
: EXECUTE-MEMBERS \ inst method --
Apply the given method to all members of the instance of a type/class.
: TWIST-STRUCTURE \ inst --
Twist structure method
: INIT-STRUCTURE \ inst --
Init structure method
In order to deal with structures and fields without having to backtrack the input stream or the execution order, an additional stage is added to the Forth parser to allow phrases of the forms:
inst.field
inst.field.field
type.field
type.field.field
to be parsed, where each item is separated by a dot character. The first item must be an instance of a type or a type. If it is an instance, the address is provided, otherwise the base address is assumed to be on the stack. Any items between the first and last item add their offsets to the address, and the last item performs the usual operation of the field as defined by an operator. For example:
type: point:
int: x0
int: y0
:m <op1> ... ;m
:m <op2> ... ;m
end-type
point: Mypoint
5 to MyPoint.x0
We might define a line as joining two points:
type: line:
point: p1
point: p2
...
end-type
line: MyLine
5 to MyLine.p2.y0
: must-be-inst-throw \ xt --
THROW because the xt is not of an instance of a type.
: class-ise-throw \ --
THROW because we have misconstructed a CLASS.
: COMP-1ST-STRUCT ** \ operator cfa flag --
Compile the first part of a dotted phrase.
Instances of TYPE: or POINTER: are the only valid cfa's.
: COMP-MIDDLE-STRUCT ** ( operator cfa flag --- )
Compile the middle portions of a dotted phrase.
Instances of TYPE: are the only valid cfas.
VFX v4 provides a hook in the interpreter loop especially for object package parsers.
: +structures runword \ --
Switch on the structure compiler.
: -structures runword \ --
Switch off the structure compiler.
VFX v5 uses recognisers for all parsers. Installing a dot notation parser is something of a kluge as the minimum has been done to make code work without a total rewrite of the parsing code.
: dotNotation? \ -- flag
Return true if the text at POCKET
appears to be a
well-formed dot notation string
2variable DotPS$ \ -- addr
Temporarily holds text string address and length.
: isDotText? \ c-addr u -- flag
Return true if the text appears to be a well-formed dot
notation string.
' doDotText ' doDotText ' postDotText RecType: r:classVFX \ -- struct
Contains the three actions for dot parsers.
: rec-classVFX \ caddr u -- r:float | r:fail
The parser part of the floating point recogniser.
: +structures runword \ --
Switch on the structure compiler.
: -structures runword \ --
Switch off the structure compiler.