Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/tools/build/v2/kernel/class.jam @ 12

Last change on this file since 12 was 12, checked in by landauf, 17 years ago

added boost

File size: 10.1 KB
Line 
1#  (C) Copyright David Abrahams 2002. Permission to copy, use, modify, sell and
2#  distribute this software is granted provided this copyright notice appears in
3#  all copies. This software is provided "as is" without express or implied
4#  warranty, and with no claim as to its suitability for any purpose.
5
6# Polymorphic class system built on top of core Jam facilities.
7#
8# Classes are defined by 'class' keywords::
9#
10#     class myclass ( arg1 )         
11#     {
12#         rule __init__ ( )          # constructor
13#         {
14#             self.attribute = $(arg1) ;
15#         }
16#
17#         rule method1 ( )           # method
18#         {
19#             return [ method2 ] ;
20#         }
21#
22#         rule method2 ( )           # method
23#         {
24#             return $(self.attribute) ;
25#         }
26#     }
27#
28# The __init__ rule is the constructor, and sets member variables.
29#
30# New instances are created by invoking [ new <class> <args...> ]::
31#
32#     local x = [ new myclass foo ] ;        # x is a new myclass object
33#     assert.result foo : [ $(x).method1 ] ; # $(x).method1 returns "foo"
34#
35# Derived class are created by mentioning base classes in the declaration::
36#
37#     class derived : myclass
38#     {
39#          rule __init__ ( arg )
40#          {
41#              myclass.__init__ $(arg) ;  # call base __init__
42#
43#          }
44#
45#          rule method2 ( )           # method override
46#          {
47#              return $(self.attribute)XXX ;
48#          }
49#     }
50#
51# All methods operate virtually, replacing behavior in the base
52# classes. For example::
53#
54#     local y = [ new derived foo ] ;           # y is a new derived object
55#     assert.result fooXXX : [ $(y).method1 ] ; # $(y).method1 returns "foo"
56#
57# Each class instance is its own core Jam module. All instance
58# attributes and methods are accessible without additional
59# qualification from within the class instance. All rules imported in
60# class declaration, or visible in base classses are also visible.
61# Base methods are available in qualified form: base-name.method-name.
62# By convention, attribute names are prefixed with "self.".
63
64import numbers ;
65import errors : * ;
66import set ;
67import modules ;
68import assert ;
69
70classes = ;
71
72
73rule xinit ( instance : class )
74{
75    module $(instance)
76    {
77        __class__ = $(2) ;
78        __name__ = $(1) ;
79    }   
80}
81
82
83rule new ( class args * : * )
84{
85    .next-instance.$(class) ?= 1 ;
86    local id = object($(class))@$(.next-instance.$(class)) ;
87   
88    xinit $(id) : $(class) ;   
89       
90    INSTANCE $(id) : class@$(class) ;
91    IMPORT_MODULE $(id) : ;
92    $(id).__init__ $(args) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
93         
94    # bump the next unique object name
95    .next-instance.$(class) = [ numbers.increment $(.next-instance.$(class)) ] ;
96   
97    # Return the name of the new instance.
98    return $(id) ;
99}
100
101
102rule bases ( class )
103{
104    #if ! ( $(class) in $(classes) )
105    #{
106    #    error class $(class) not defined ;
107    #}
108   
109    module class@$(class)
110    {
111        return $(__bases__) ;
112    }
113}
114
115rule is-derived ( class : bases + )
116{
117    #local all = $(class) $(bases) ;
118    #if ! ( $(all) in $(classes) )
119    #{
120    #    error class(es) [ set.difference $(class) $(bases) : $(classes) ] not defined ;
121    #}
122   
123    local stack = $(class) ;
124    local visited found ;
125    while ( ! $(found) ) && $(stack)
126    {
127        local top = $(stack[1]) ;
128        stack = $(stack[2-]) ;
129        if ! ( $(top) in $(visited) )
130        {
131            visited += $(top) ;
132            stack += [ bases $(top) ] ;
133           
134            if $(bases) in $(visited)
135            {
136                found = true ;
137            }
138        }
139    }
140    return $(found) ;
141}
142
143# Returns true if the 'value' is a class instance.
144rule is-instance ( value # The value to check
145     )
146{
147    return [ MATCH "^(object\\()[^@]+\\)@.*" : $(value) ] ;       
148}
149
150# Check if the given value is of the given type.
151#
152rule is-a (
153    instance # The value to check.
154    : type # The type to test for.
155    )
156{
157    if [ is-instance $(instance) ]
158    {
159        return [ class.is-derived [ modules.peek $(instance) : __class__ ] : $(type) ] ;
160    }
161}
162
163local rule typecheck ( x )
164{
165    local class-name = [ MATCH "^\\[(.*)\\]$" : [ BACKTRACE 1 ] ] ;
166    if ! [ is-a $(x) : $(class-name) ]
167    {
168        return "Expected an instance of "$(class-name)" but got \""$(x)"\" for argument" ;
169    }
170}
171
172local rule __test__ ( )
173{
174    import "class" : * ;
175    import assert ;
176    import errors : * ;
177
178    # This will be the construction function for a class called
179    # 'myclass'
180    class myclass
181    {
182        import assert : nonempty-variable ;       
183       
184        rule __init__ ( x_ * : y_ * )
185        {           
186            # set some instance variables
187            x = $(x_) ;
188            y = $(y_) ;
189            foo += 10 ;
190        }
191       
192        rule set-x ( newx * )
193        {
194            x = $(newx) ;
195        }
196
197        rule get-x ( )
198        {
199            return $(x) ;
200        }
201
202        rule set-y ( newy * )
203        {
204            y = $(newy) ;
205        }
206
207        rule get-y ( )
208        {
209            return $(y) ;
210        }
211
212        rule f ( )
213        {
214            return [ g $(x) ] ;
215        }
216
217        rule g ( args * )
218        {
219            if $(x) in $(y)
220            {
221                return $(x) ;
222            }
223            else if $(y) in $(x)
224            {
225                return $(y) ;
226            }
227            else
228            {
229                return ;
230            }
231        }
232
233        rule get-class ( )
234        {
235            return $(__class__) ;
236        }
237       
238        rule get-instance ( )
239        {
240            return $(__name__) ;
241        }
242       
243        rule invariant ( )
244        {
245            assert.equal 1 : 1 ;
246        }               
247       
248        rule get-foo ( )
249        {
250            return $(foo) ;
251        }                       
252    }
253#    class myclass ;
254
255    class derived1 : myclass
256    {       
257        rule __init__ ( z_ )
258        {
259            myclass.__init__ $(z_) : X ;
260            z = $(z_) ;           
261        }
262       
263        # override g
264        rule g ( args * )
265        {
266            return derived1.g ;
267        }
268
269        rule h ( )
270        {
271            return derived1.h ;
272        }
273
274        rule get-z ( )
275        {
276            return $(z) ;
277        }
278
279        # Check that 'assert.equal' visible in base class is visible
280        # here.
281        rule invariant2 ( )
282        {
283            assert.equal 2 : 2 ;
284        }               
285       
286        # Check that 'nonempty-variable' visible in base class is
287        # visible here.
288        rule invariant3 ( )
289        {
290            local v = 10 ;
291            nonempty-variable v ;
292        }               
293    }
294#    class derived1 : myclass ;
295
296    class derived2 : myclass
297    {
298        rule __init__ ( )
299        {           
300            myclass.__init__ 1 : 2 ;
301        }
302       
303        # override g
304        rule g ( args * )
305        {
306            return derived2.g ;
307        }
308
309         rule get-x ( )
310         {
311             # Test the ability to call base class functions with qualification.
312             return [ myclass.get-x ] ;
313         }
314    }
315#    class derived2 : myclass ;
316
317    class derived2a : derived2
318    {
319        rule __init__
320        {
321            derived2.__init__ ;
322        }               
323    }
324#    class derived2a : derived2 ;
325
326    local rule expect_derived2 ( [derived2] x ) { }
327
328    local a = [ new myclass 3 4 5 : 4 5 ] ;
329    local b = [ new derived1 4 ] ;
330    local b2 = [ new derived1 4 ] ;
331    local c = [ new derived2 ] ;
332    local d = [ new derived2 ] ;
333    local e = [ new derived2a ] ;
334
335    expect_derived2 $(d) ;
336    expect_derived2 $(e) ;
337
338    # argument checking is set up to call exit(1) directly on
339    # failure, and we can't hijack that with try, so we'd better
340    # not do this test by default.  We could fix this by having
341    # errors look up and invoke the EXIT rule instead; EXIT can be
342    # hijacked (;-)
343    if --fail-typecheck in [ modules.peek : ARGV ]
344    {
345        try ;
346        {
347            expect_derived2 $(a) ;
348        }
349        catch
350          "Expected an instance of derived2 but got" instead
351          ;
352    }
353
354
355    #try ;
356    #{
357    #    new bad_subclass ;
358    #}
359    #catch
360    #  bad_subclass.bad_subclass failed to call base class constructor myclass.__init__
361    #  ;
362
363    #try ;
364    #{
365    #    class bad_subclass ;
366    #}
367    #catch bad_subclass has already been declared ;
368
369    assert.result 3 4 5 : $(a).get-x ;
370    assert.result 4 5 : $(a).get-y ;
371    assert.result 4 : $(b).get-x ;
372    assert.result X : $(b).get-y ;
373    assert.result 4 : $(b).get-z ;
374    assert.result 1 : $(c).get-x ;
375    assert.result 2 : $(c).get-y ;
376    assert.result 4 5 : $(a).f ;
377    assert.result derived1.g : $(b).f ;
378    assert.result derived2.g : $(c).f ;
379    assert.result derived2.g : $(d).f ;
380   
381    assert.result 10 : $(b).get-foo ;
382   
383    $(a).invariant ;
384    $(b).invariant2 ;
385    $(b).invariant3 ;
386   
387    # Check that the __class__  attribute is getting properly set.
388    assert.result myclass : $(a).get-class ;
389    assert.result derived1 : $(b).get-class ;
390    assert.result $(a) : $(a).get-instance ;
391
392    $(a).set-x a.x ;
393    $(b).set-x b.x ;
394    $(c).set-x c.x ;
395    $(d).set-x d.x ;
396    assert.result a.x : $(a).get-x ;
397    assert.result b.x : $(b).get-x ;
398    assert.result c.x : $(c).get-x ;
399    assert.result d.x : $(d).get-x ;
400
401    class derived3 : derived1 derived2
402    {
403        rule __init__ ( )
404        {
405        }
406    }
407   
408
409    assert.result : bases myclass ;
410    assert.result myclass : bases derived1 ;
411    assert.result myclass : bases derived2 ;
412    assert.result derived1 derived2 : bases derived3 ;
413
414    assert.true is-derived derived1 : myclass ;
415    assert.true is-derived derived2 : myclass ;
416    assert.true is-derived derived3 : derived1 ;
417    assert.true is-derived derived3 : derived2 ;
418    assert.true is-derived derived3 : derived1 derived2 myclass ;
419    assert.true is-derived derived3 : myclass ;
420
421    assert.false is-derived myclass : derived1 ;
422
423    assert.true is-instance $(a) ;
424    assert.false is-instance bar ;
425
426    assert.true is-a $(a) : myclass ;
427    assert.true is-a $(c) : derived2 ;
428    assert.true is-a $(d) : myclass ;
429    assert.false is-a literal : myclass ;
430}
Note: See TracBrowser for help on using the repository browser.