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