-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCLASS
761 lines (584 loc) · 24.1 KB
/
CLASS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
\ $Id: class.f 1.1 1994/04/01 07:52:15 andrew Exp $
comment:
This file introduces object orientation to WimpForth.
You declare a new class by
:class newcl <super parent
...
;class
where parent is an existing class (only one!!)
In between you define instance variables
int flag
and methods
:m SetFlag: ( f -- ) to flag ;m
Available are all methods and instance variable of the parent class.
Until now this is like a type declaration. You define an object of
this class like that:
newcl newobj
newobj is now ready to receive messages: (=invocation of the methods)
0 SetFlag: newobj
The first message (ClassInit:) was automatically sent to it when
newobj was created 2 lines before. (This is part of the system!)
There are more aspects of the problem. Extensive use of classes
is made in the Wimp interface of WimpForth. Please have a look
at the files "icons", "windows", "menus", "controls" and for
defining objects at the file "extend".
comment;
only forth also definitions
0 Value NewObject \ Newest object being created
locals-on
\ : h. ( u -- ) base @ hex swap u. base ! ;
: @word ( -<word>- addr ) bl word dup count upper ;
: hash> ( -<word>- ) @word count hash ;
classes also definitions
\ -------------------- Selectors --------------------
: ?is.Sel ( addr -- addr f ) \ true if word at Here is a selector object.method
dup count dup>r ':' scan 0> \ found it and
nip r> 3 > and ; \ longer than 3
: ?isSel dup dup c@ + c@ ascii : = ;
: >selector ( str -- SelID ) \ get a selector from the input stream
?isSel 0= abort" not a selector" count hash ;
: getSelect ( -- SelID ) \ get a selector from the input stream
@word >selector ;
\ -------------------- Class Structure --------------------
0 value ^Class \ pointer to class being defined
\ references are from class pfa
: MFA [ voc-pfa-size 0 cells+ ] literal + ; \ method dictionary
: IFA [ voc-pfa-size 1 cells+ ] literal + ; \ instance variable dictionary
: DFA [ voc-pfa-size 2 cells+ ] literal + ; \ data area
: XFA [ voc-pfa-size 3 cells+ ] literal + ; \ width of indexed items
: SFA [ voc-pfa-size 4 cells+ ] literal + ; \ pointer to superclass
: FLAGS [ voc-pfa-size 5 cells+ ] literal + ; \ flags
voc-pfa-size 6 cells+ constant class-size \ size of class pfa
: >obj ( objCfa -- ^obj ) >body cell+ ;
: >class ( ^obj -- ^class ) CELL - @ ;
: classpointer? ( class -- f ) FLAGS @ 1 AND ;
: class-allot ( n -- ) ^class DFA +! ;
\ -------------------- Find Methods --------------------
: (FINDM) ( SelID ^class -- m0cfa ) \ find method in a class
2dup
MFA ((findm)) if nip nip exit then
\ over 0 <# #s #> temp$ place
\ s" " temp$ +place
swap unhash temp$ place
S" not understood by class " temp$ +place
body> >name nfa-count temp$ +place
temp$ msg ! -2 throw ;
create null-obj-buf 260 allot
: FIND-METHOD ( SelID ^obj -- ^obj m0cfa ) \ find method in object
?dup 0= abort" Null Object"
tuck >class (findm) ;
: (Defer) ( ^obj -- ) \ look up SelID at IP and run the method
@(ip) swap ( SelID ^obj )
Find-Method execute ;
: dbg-next-cell-class ( ip cfa -- ip' cfa )
dup ['] (Defer) =
if swap cell+ swap
then ;
dbg-next-cell chain-add dbg-next-cell-class \ link into the debugger
: dbg-nest-class ( top-of-user-stack cfa flag -- cfa false | true )
dup ?exit \ leave if already found
over ['] (Defer) =
if 2drop cr .s
\ !!! USES A COPY OF THE ADDRESS ON TOP OF THE STACK TO LOCATE THE METHOD !!!
[ bug ] ip @ cell+ @ over Find-Method nip 3 cells+ ip !
2 nesting +!
true
then ;
classes
dbg-nest-chain chain-add dbg-nest-class
: .word-type-class ( cfa flag -- cfa false | true )
dup ?exit
over ['] (Defer) =
if 2drop
." Late: "
true
then ;
.word-type-chain chain-add .word-type-class
: .execution-class-class ( ip cfa flag -- ip' cfa flag )
dup ?exit \ leave if non-zero flag
over ['] (Defer) = \ is it a late bound method
if drop \ discard original flag
." [[ " swap cell+
dup @ unhash type
cell+ swap ." ]] "
true \ return true if we handled it
then ;
.execution-class-chain chain-add .execution-class-class
0 Value ^Self
0 Value ^Super \ nfa of SUPER pseudo-Ivar
1 Value rangeCheck \ true if runtime range check desired
' find @ constant doDefer \ Defer cfa
: ?isClass ( cfa -- f ) call@ dup doCLass =
swap do|Class = or ;
: ?isObj ( cfa -- f ) call@ doObj = ;
: ?isValue ( cfa -- f ) call@ doValue = ;
: ?isVect ( cfa -- f ) call@ dup doValue =
over doDefer = or
swap (iv@) = or ;
: ?isParen ( cfa -- f ) >name nfa-count drop c@ ascii [ = ;
\ ERROR if not compiling a new class definition
: ?Class ^class 0= abort" Not in a class" ;
\ Determine if next word is an instance var.
\ Return pointer to class field in ivar structure.
: vFind ( str -- str f OR ^iclass t )
^class
IF dup count hash ^class IFA ((findm))
dup if rot drop then
ELSE 0
THEN ;
: IDX-HDR ( #elems ^class OR ^class -- indlen )
XFA @ DUP IF 2DUP ( width ) W, ( #elems ) W, * THEN ;
\ -------------------- Initialize Instance Variables --------------------
((
Instance variable consists of four 4-byte fields. A fifth field is
used for indexed ivars only.
Offset Name Description
------ ---- ---------------------------------------
0 0 link points to link of next ivar in chain
4 1 name 32-bit hash value of name
8 2 class pointer to class pfa
12 3 offset offset in object to start of ivar data
16 4 #elem number of elemens (indexed ivars only)
In the stack diagrams, "ivar" refers to the starting address of this
structure. The IFA field of a class points to the first ivar.
))
: iclass ( ivar -- 'class ) 2 cells + ;
: @IvarOffs ( ivar -- offset ) 3 cells + @ ;
: @IvarElems ( ivar -- #elems ) 4 cells + @ ;
\ send ClassInit: message to ivar on stack
: InitIvar ( ivar offset -- )
over @IvarOffs + newObject + ( ivar addr )
[ getSelect ClassInit: ] literal
rot iclass @ (findm) execute ;
\ ITRAV traverses the tree of nested ivar definitions in a class,
\ building necessary indexed area headers.
: ITRAV { ivar offset -- }
Begin
ivar ^Self <>
While
ivar iclass @ IFA @
ivar @IVarOffs offset + RECURSE
ivar iclass @ ?dup ( Why would an Ivar have no class ?? )
if dup classpointer?
if newObject offset + ivar @IvarOffs +
( ^class ivarAddr )
2dup cell - ! \ store class pointer
over XFA @
if over DFA @ + \ addr of indexed area
swap XFA @ over W! \ Index width
ivar @IvarElems swap 2 + w! \ #elems
else 2drop
then
else drop
then
ivar offset initIvar \ send ClassInit:
then
ivar @ to ivar \ next ivar in chain
Repeat ;
defer ClassInit ( -- ) \ send ClassInit: to newObject
' noop is classinit
\ ( #elems ^class OR ^class -- ) Compile an instance variable dictionary entry
: <VAR
@word Vfind abort" Duplicate Instance Variable"
dup count 2dup hash add-hash
^Class IFA link, \ link
count hash , \ name hash
dup , \ class
dup ClassPointer?
if 4 class-allot then \ if indexed, save 4 for class ptr
^class DFA @ , \ offset
dup XFA @ dup
if rot dup , * 4 + then \ #elems
swap DFA @ + \ Account for named ivar lengths
class-allot ;
: (|Build) ( #elems ^class OR ^class -- ) \ Build an instance of a class
^class
IF <Var \ build an ivar
ELSE doObj call, \ cfa
dup , \ class
here to newObject
dup DFA @ reserve \ allot space for ivars
dup IDX-HDR reserve \ allot space for indexed data
IFA @ 0 ITRAV \ init instance variables
ClassInit \ send CLASSINIT: message
THEN ;
: (Build) ( #elems ^class OR ^class -- ) \ Build an instance of a class
^class
IF <Var \ build an ivar
ELSE
>in @
@word (find)
if dup ?isValue
if \ create headerless object and store
\ address in a value
here >obj swap cell+ ( 1cfa ) execute
drop ( >in )
else
\ redefinition
drop >in ! header
\ cr last @ .id ." is redefined "
then
else
\ new object
drop >in ! header
then
doObj call, \ cfa
dup , \ class
here to newObject
dup DFA @ reserve \ allot space for ivars
dup IDX-HDR reserve \ allot space for indexed data
IFA @ 0 ITRAV \ init instance variables
ClassInit \ send CLASSINIT: message
THEN ;
create obj-buf 260 allot
: (Obj-Build) ( #elems ^class OR ^class -- ) \ Build an instance of a class
obj-buf count upper (find)
if dup ?isValue
if \ create headerless object and store
\ address in a value
here >obj swap cell+ ( 1cfa ) execute
else
\ redefinition
obj-buf count "header
then
else
\ new object
obj-buf count "header
then
doObj call, \ cfa
dup , \ class
here to newObject
dup DFA @ reserve \ allot space for ivars
dup IDX-HDR reserve \ allot space for indexed data
IFA @ 0 ITRAV \ init instance variables
ClassInit \ send CLASSINIT: message
;
\ -------------------- Heap Objects --------------------
\ build a new object on the heap for class. Use: Heap> className
\ gets heap, and returns ptr.
: (heapObj) { theClass \ dLen obAddr idWid #els -- } 0 to #els
theClass dfa @ to dlen theClass XFA @ to idWid
idWid IF to #els THEN
dLen cell+ idWid IF idWid #els * cell+ + THEN \ get total length of obj
allocate abort" Out of Memory"
theClass over ! \ create the class ptr
cell+ to obAddr \ get nonReloc heap, save ptr to cfa
idWid IF obAddr dLen + idWid over w! 2 + #els swap w! THEN
obAddr to newObject theClass IFA @ 0 Itrav
classinit obAddr ;
: heap> ( -- addr )
' dup ?isClass not abort" Use: heap> classname " >body
state @
IF [compile] literal
Compile (heapObj) ELSE (heapObj)
THEN
; Immediate
\ --------------- Build SUPER and SELF pseudo ivars ---------------
S" SUPER" hash> SUPER add-hash
Here to ^Super
0 , \ link
hash> SUPER , \ name
0 , \ class
0 , \ offset (was -1)
S" SELF" hash> SELF add-hash
Here to ^Self
^Super , \ link
hash> SELF , \ name
0 , \ class
0 , \ offset (was -1)
^Self ' classes >body IFA ! \ latest ivar
\ -------------------- Create a new Class --------------------
0 value oldcurrent
\ Build a class header with its superclass pointer
: inherit ( pfa -- )
dup here class-size move \ copy class data
here body> vcfa>voc voc>vlink
voc-link @ over !
voc-link !
class-size allot \ reserve rest of class data
dup ^Class SFA ! \ store pointer to superclass
^Super iclass ! \ store superclass in SUPER
^Class ^Self iclass ! \ store my class in SELF
\ add to search order
also ^class body> vcfa>voc context ! definitions ;
forth definitions
here 0 , value Obj-CLASS
0 value Obj-LOADLINE
: :Object ( -<object-name>- )
bl word count 255 min obj-buf place
current @ to oldcurrent \ save context for later restoral
false to ?:M
doClass , \ dummy filler to fool the system
\ into thinking this is a definition
here to Obj-CLASS
here to ^Class
0 op! \ for error checking in runIvarRef
?loading @
if loadline @
else -1
then to Obj-LOADLINE ;
: :Class ( -- )
current @ to oldcurrent \ save context for later restoral
false to ?:M
create
here to ^Class
0 op! \ for error checking in runIvarRef
does>
[ here 8 - to doClass ] \ a dirty trick!
(Build) ;
: <Super ( -- ) \ allow inheriting from a class or an object
' dup ?isClass
if >body inherit
else dup ?isObj 0= abort" not a class or object"
>body @ inherit
then ;
synonym <Object <Super
synonym <Class <Super
: |Class ( -- )
current @ to oldcurrent \ save context for later restoral
false to ?:M
create
here to ^Class
0 op! \ for error checking in runIvarRef
does>
[ here 8 - to do|Class ] \ a dirty trick!
(|Build) ;
classes definitions
: ;Class ( -- )
0 ^Super iclass !
0 ^Self iclass !
0 to ^Class
forth definitions previous
oldcurrent ?dup
if current !
0 to oldcurrent
then ;
: ;Object ( -- )
0 ^Super iclass !
0 ^Self iclass !
0 to ^Class
forth definitions previous
oldcurrent ?dup
if current !
0 to oldcurrent
then Obj-CLASS (Obj-Build)
Obj-LOADLINE last @ name> >view ! ;
\ -------------------- Method Compiler --------------------
: method ( SelID -- ) \ Build a methods dictionary entry for selector
?Class ?Exec
dup pocket count rot add-hash
^Class MFA link, \ link
, \ name is selector's hashed value
m0cfa call, \ build methods cfas
m1cfa call,
0 , \ #locals & #args
!csp ] ; \ start compiler
\ For Windows messages, we would like the selector to be a constant
\ defined as the Window message number. :M will support both types of
\ selectors.
260 constant unres-len
create unres-methods unres-len allot
unres-methods unres-len erase
: :M ( -- )
\ cr ." Method " >in @ bl word count type >in !
unres-methods unres-len erase \ pre-clear unresolved methods array
@word (find)
if execute ( word must return selector value )
else >selector
then method
true to ?:M ; immediate \ mark as making a new method
: ;M ( -- )
?:M 0= abort" Methods must START with :M !"
false to ?:M
?csp
postpone unnestm
postpone [
0 to Parms
semicolon-chain do-chain
voc-also \ don't add to hash table
; Immediate
\ create make-amethod-buf 64 allot
: resolve-methods ( -- )
unres-methods
begin count dup
while 2dup
2dup hash add-hash +
repeat 2drop
unres-methods unres-len erase ;
\ -------------------- Object Compiler --------------------
\ Key to instantiation actions
\ 0 = notFnd -not previously defined (not used)
\ 1 = objTyp -defined as an object
\ 2 = classTyp -as a class
\ 3 = vecTyp -as an object vector (value or defer)
\ 4 = parmTyp -as a named parm
\ 5 = parenType -open paren for defer group
\ ( str -- cfa tokenID ) Determine type of token referenced by string.
: refToken
pFind if 4 exit then
(find) 0= ?missing
dup ?IsObj if 1 exit then
dup ?IsClass if 2 exit then
dup ?IsVect if 3 exit then
dup ?IsParen if 5 exit then
1 abort" Invalid object type" ;
: ivarRef ( selID ^iclass -- ) \ compile ivar reference
cell+ Find-Method >body , @ , ; \ | 1cfa | offs |
: runIvarRef ( selID ^iclass -- ) \ run ivar reference (DEBUG ONLY!!)
^base 0= abort" No object exposed"
cell+ Find-Method
swap @ ( offset ) ^base + swap execute ;
0 value pSel ( selector for [[ and ]] ) ( NOTE: NO NESTING!! )
\ ( selID $str -- ) Build a reference to an object or vector
: objRef
Case refToken
0 ( ? ) of abort endof
1 ( object ) of dup , >obj find-method , drop endof
2 ( class ) of >body (findm) , endof
3 ( vector ) of , compile (defer) , endof
4 ( parm ) of , compile (defer) , endof
5 ( paren ) of drop to pSel 251 endof
Endcase ;
\ ( selPfa $str -- ) Execute using token in stream
: runRef
Case refToken
0 ( ? ) of abort endof
1 ( object ) of >obj find-method endof
2 ( class ) of >body (findm) endof
3 ( vector ) of execute find-method endof
4 ( parm ) of abort endof
5 ( paren ) of drop to pSel ['] noop endof
Endcase
execute ( execute m0cfa ) ;
\ ================= Selector support ==========================
: _do_message ( val string -- )
STATE @
IF
VFIND \ instance variable?
IF ivarRef \ ivar reference
ELSE objRef \ compile object/vector reference
THEN
ELSE
VFIND
IF runIvarRef ( Debug only )
ELSE runRef \ run state - execute object/vector ref
THEN
THEN ;
-1 value method_hval
create method_hstring name-max-chars 2 + allot
\ message is the message compiler invoked by using a selector
: do_message ( -- )
@word count name-max-chars min method_hstring place
method_hval method_hstring _do_message ; Immediate
: _msgFind ( addr -- addr false | cfa true )
?isSel
if count name-max-chars min 2dup hash dup ?unhash
if nip nip
else >r unres-methods
begin dup c@
while count +
repeat 2dup + 1+ \ end of string
unres-methods unres-len + > \ beyond end?
abort" Unresolved Methods buffer overflow!"
place
r>
then to method_hval ['] do_message
1 EXIT
then
0 ;
\ msgFind is the new action for find. We look in the following order:
\ 1. Local variables
\ 2. Forth Dictionary (full search order)
\ 3. If word ends in ":" treat it as a selector
: msgFind ( addr -- addr false | cfa true )
pfind ?dup if exit then
(find) ?dup if exit then
_msgFind ;
' msgfind is find
: _classInit ( -- ) CLASSINIT: newObject ;
' _classInit is ClassInit
\ -------------------- Late Binding --------------------
\ Force late binding of method to object, as in SmallTalk
\ a close bracket gets the last selID from pSel and
\ compiles a defer: selID. This will build a deferred reference to the
\ parenthesized group.
: ]] State @
IF 251 ?Pairs
Compile (Defer) pSel ,
ELSE
pSel swap Find-Method execute
THEN
; Immediate
\ left bracket has no meaning unless preceded by a selector.
: [[ true abort" [[ must be preceeded by a selector " ; immediate
\ Force a class pointer to be compiled when the object is used as
\ an instance variable. This is so that we can receive late-bound
\ messages.
: <ClassPointer ( -- ) 1 ^Class FLAGS ! ;
\ Set a class and its subclasses to indexed
: <Indexed ( width -- ) ?Class ^Class XFA ! <ClassPointer ;
\ Compile a self reference, but only if the class is guaranteed to
\ have a class pointer. We can send ourself late-bound messages
\ with the syntax: Msg: [[ self ]]
: Self ( -- addr )
^Class ClassPointer? 0= abort" Must use <Indexed or <ClassPointer"
compile ^base ; immediate
\ -------------------- Instance Variables --------------------
: bytes ( n -- )
create ^class DFA @ , class-allot
does> @ ^base + ;
: int ( -- )
header
(iv@) call,
^Class DFA @ ,
(iv!) call,
(iv+!) call,
cell class-allot ;
: int-array ( size -- )
header
(iv[]@) call,
^Class DFA @ ,
(iv[]!) call,
(iv[]+!) call,
cells class-allot ;
: &> ( -- )
r> lcount cell+ @ ^base + swap >r ;
: dispose ( addr -- )
~: [[ dup ]] cell- free abort" Disposing Object failed!" ;
\ -------------------- Base Class "Object" --------------------
forth definitions
:Class object ' classes >body classes inherit
:M ClassInit: ;M
:M ~: ;M
:M Addr: ( -- addr ) ^base ;M
:M Print: ( -- ) ." Object@" ^base . ;M
unres-methods unres-len erase
semicolon-chain chain-add resolve-methods \ link into definition completion
;Class
\ -------------------- Debugging Tools --------------------
0 op! ( to help catch incorrect use of expose. See runIvarRef. )
((
: expose ( expose vocabulary of class or object )
' dup ?isClass
if
>body ( ^class )
0 op! ( no object to send messages to! )
else
dup ?isObj not abort" Not an object or a class"
>obj dup op! ( make current )
>class
then
to ^class \ reset current class
^Class context ! \ add to search order
^Class SFA @ ^Super iclass ! \ store superclass in SUPER
^Class ^Self iclass ! \ store my class in SELF
;
: unexpose ( -- )
0 ^Super iclass !
0 ^Self iclass !
0 to ^Class
0 op!
forth definitions ;
))
only forth also definitions
cr .( Class loaded )