blob: b8e55af3550ba35b26b6ea43ab25642a2410e004 (
plain)
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
|
# Copyright 2001, 2002, 2003 Dave Abrahams
# Copyright 2002, 2005 Rene Rivera
# Copyright 2002, 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# Polymorphic class system built on top of core Jam facilities.
#
# Classes are defined by 'class' keywords::
#
# class myclass
# {
# rule __init__ ( arg1 ) # constructor
# {
# self.attribute = $(arg1) ;
# }
#
# rule method1 ( ) # method
# {
# return [ method2 ] ;
# }
#
# rule method2 ( ) # method
# {
# return $(self.attribute) ;
# }
# }
#
# The __init__ rule is the constructor, and sets member variables.
#
# New instances are created by invoking [ new <class> <args...> ]:
#
# local x = [ new myclass foo ] ; # x is a new myclass object
# assert.result foo : [ $(x).method1 ] ; # $(x).method1 returns "foo"
#
# Derived class are created by mentioning base classes in the declaration::
#
# class derived : myclass
# {
# rule __init__ ( arg )
# {
# myclass.__init__ $(arg) ; # call base __init__
#
# }
#
# rule method2 ( ) # method override
# {
# return $(self.attribute)XXX ;
# }
# }
#
# All methods operate virtually, replacing behavior in the base classes. For
# example::
#
# local y = [ new derived foo ] ; # y is a new derived object
# assert.result fooXXX : [ $(y).method1 ] ; # $(y).method1 returns "foo"
#
# Each class instance is its own core Jam module. All instance attributes and
# methods are accessible without additional qualification from within the class
# instance. All rules imported in class declaration, or visible in base classses
# are also visible. Base methods are available in qualified form:
# base-name.method-name. By convention, attribute names are prefixed with
# "self.".
import modules ;
import numbers ;
rule xinit ( instance : class )
{
module $(instance)
{
__class__ = $(2) ;
__name__ = $(1) ;
}
}
rule new ( class args * : * )
{
.next-instance ?= 1 ;
local id = object($(class))@$(.next-instance) ;
xinit $(id) : $(class) ;
INSTANCE $(id) : class@$(class) ;
IMPORT_MODULE $(id) ;
$(id).__init__ $(args) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
# Bump the next unique object name.
.next-instance = [ numbers.increment $(.next-instance) ] ;
# Return the name of the new instance.
return $(id) ;
}
rule bases ( class )
{
module class@$(class)
{
return $(__bases__) ;
}
}
rule is-derived ( class : bases + )
{
local stack = $(class) ;
local visited found ;
while ! $(found) && $(stack)
{
local top = $(stack[1]) ;
stack = $(stack[2-]) ;
if ! ( $(top) in $(visited) )
{
visited += $(top) ;
stack += [ bases $(top) ] ;
if $(bases) in $(visited)
{
found = true ;
}
}
}
return $(found) ;
}
# Returns true if the 'value' is a class instance.
#
rule is-instance ( value )
{
return [ MATCH "^(object\\()[^@]+\\)@.*" : $(value) ] ;
}
# Check if the given value is of the given type.
#
rule is-a (
instance # The value to check.
: type # The type to test for.
)
{
if [ is-instance $(instance) ]
{
return [ class.is-derived [ modules.peek $(instance) : __class__ ] : $(type) ] ;
}
}
local rule typecheck ( x )
{
local class-name = [ MATCH "^\\[(.*)\\]$" : [ BACKTRACE 1 ] ] ;
if ! [ is-a $(x) : $(class-name) ]
{
return "Expected an instance of "$(class-name)" but got \""$(x)"\" for argument" ;
}
}
rule __test__ ( )
{
import assert ;
import "class" : new ;
# This will be the construction function for a class called 'myclass'.
#
class myclass
{
import assert ;
rule __init__ ( x_ * : y_ * )
{
# Set some instance variables.
x = $(x_) ;
y = $(y_) ;
foo += 10 ;
}
rule set-x ( newx * )
{
x = $(newx) ;
}
rule get-x ( )
{
return $(x) ;
}
rule set-y ( newy * )
{
y = $(newy) ;
}
rule get-y ( )
{
return $(y) ;
}
rule f ( )
{
return [ g $(x) ] ;
}
rule g ( args * )
{
if $(x) in $(y)
{
return $(x) ;
}
else if $(y) in $(x)
{
return $(y) ;
}
else
{
return ;
}
}
rule get-class ( )
{
return $(__class__) ;
}
rule get-instance ( )
{
return $(__name__) ;
}
rule invariant ( )
{
assert.equal 1 : 1 ;
}
rule get-foo ( )
{
return $(foo) ;
}
}
# class myclass ;
class derived1 : myclass
{
rule __init__ ( z_ )
{
myclass.__init__ $(z_) : X ;
z = $(z_) ;
}
# Override g.
#
rule g ( args * )
{
return derived1.g ;
}
rule h ( )
{
return derived1.h ;
}
rule get-z ( )
{
return $(z) ;
}
# Check that 'assert.equal' visible in base class is visible here.
#
rule invariant2 ( )
{
assert.equal 2 : 2 ;
}
# Check that 'assert.variable-not-empty' visible in base class is
# visible here.
#
rule invariant3 ( )
{
local v = 10 ;
assert.variable-not-empty v ;
}
}
# class derived1 : myclass ;
class derived2 : myclass
{
rule __init__ ( )
{
myclass.__init__ 1 : 2 ;
}
# Override g.
#
rule g ( args * )
{
return derived2.g ;
}
# Test the ability to call base class functions with qualification.
#
rule get-x ( )
{
return [ myclass.get-x ] ;
}
}
# class derived2 : myclass ;
class derived2a : derived2
{
rule __init__
{
derived2.__init__ ;
}
}
# class derived2a : derived2 ;
local rule expect_derived2 ( [derived2] x ) { }
local a = [ new myclass 3 4 5 : 4 5 ] ;
local b = [ new derived1 4 ] ;
local b2 = [ new derived1 4 ] ;
local c = [ new derived2 ] ;
local d = [ new derived2 ] ;
local e = [ new derived2a ] ;
expect_derived2 $(d) ;
expect_derived2 $(e) ;
# Argument checking is set up to call exit(1) directly on failure, and we
# can not hijack that with try, so we should better not do this test by
# default. We could fix this by having errors look up and invoke the EXIT
# rule instead; EXIT can be hijacked (;-)
if --fail-typecheck in [ modules.peek : ARGV ]
{
try ;
{
expect_derived2 $(a) ;
}
catch
"Expected an instance of derived2 but got" instead
;
}
#try ;
#{
# new bad_subclass ;
#}
#catch
# bad_subclass.bad_subclass failed to call base class constructor myclass.__init__
# ;
#try ;
#{
# class bad_subclass ;
#}
#catch bad_subclass has already been declared ;
assert.result 3 4 5 : $(a).get-x ;
assert.result 4 5 : $(a).get-y ;
assert.result 4 : $(b).get-x ;
assert.result X : $(b).get-y ;
assert.result 4 : $(b).get-z ;
assert.result 1 : $(c).get-x ;
assert.result 2 : $(c).get-y ;
assert.result 4 5 : $(a).f ;
assert.result derived1.g : $(b).f ;
assert.result derived2.g : $(c).f ;
assert.result derived2.g : $(d).f ;
assert.result 10 : $(b).get-foo ;
$(a).invariant ;
$(b).invariant2 ;
$(b).invariant3 ;
# Check that the __class__ attribute is getting properly set.
assert.result myclass : $(a).get-class ;
assert.result derived1 : $(b).get-class ;
assert.result $(a) : $(a).get-instance ;
$(a).set-x a.x ;
$(b).set-x b.x ;
$(c).set-x c.x ;
$(d).set-x d.x ;
assert.result a.x : $(a).get-x ;
assert.result b.x : $(b).get-x ;
assert.result c.x : $(c).get-x ;
assert.result d.x : $(d).get-x ;
class derived3 : derived1 derived2
{
rule __init__ ( )
{
}
}
assert.result : bases myclass ;
assert.result myclass : bases derived1 ;
assert.result myclass : bases derived2 ;
assert.result derived1 derived2 : bases derived3 ;
assert.true is-derived derived1 : myclass ;
assert.true is-derived derived2 : myclass ;
assert.true is-derived derived3 : derived1 ;
assert.true is-derived derived3 : derived2 ;
assert.true is-derived derived3 : derived1 derived2 myclass ;
assert.true is-derived derived3 : myclass ;
assert.false is-derived myclass : derived1 ;
assert.true is-instance $(a) ;
assert.false is-instance bar ;
assert.true is-a $(a) : myclass ;
assert.true is-a $(c) : derived2 ;
assert.true is-a $(d) : myclass ;
assert.false is-a literal : myclass ;
}
|