Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/tools/build/v2/kernel/class.jam @ 45

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

updated boost from 1_33_1 to 1_34_1

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