1 #!/usr/bin/env python 2 3 """ 4 RSVP instruction and serialisation classes. 5 6 Copyright (C) 2007, 2008, 2009, 2010 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from micropython.data import Attr, Const, Class, Function, Module 23 from micropython.program import Block, DataObject, DataValue 24 from micropython.raw import RawObject 25 26 def name(attr): 27 if isinstance(attr, Attr): 28 return attr.name or "<unnamed>" 29 elif isinstance(attr, (Block, Const)): 30 return attr 31 else: 32 return attr.full_name() or "<unnamed>" 33 34 # Serialisation-related classes. 35 36 class RSVPObject(RawObject): 37 38 "A generic data object wrapper." 39 40 def _finalise_location(self, with_builtins): 41 42 """ 43 Set the code body location for items now that the code blocks have been 44 positioned. 45 """ 46 47 item = self.item 48 49 if not self.is_generated(with_builtins): 50 item.code_body_location = item.full_name() 51 else: 52 item.code_body_location = item.get_body_block().location 53 54 class RSVPAttr(RSVPObject): 55 56 "A wrapper for attributes/values." 57 58 def as_raw(self, objtable, paramtable, with_builtins): 59 item = self.item 60 return [ 61 DataValue( 62 item.get_context() and item.get_context().location, 63 item.get_value() and item.get_value().location 64 ) 65 ] 66 67 class RSVPBlock(RSVPObject): 68 69 "A wrapper for blocks." 70 71 def set_location(self, location, with_builtins): 72 item = self.item 73 item.location = location 74 return location + len(item.code) 75 76 def as_raw(self, objtable, paramtable, with_builtins): 77 return self.item.code 78 79 class RSVPClass(RSVPObject): 80 81 "A wrapper for classes." 82 83 def set_location(self, location, with_builtins): 84 self.item.instance_template_location = location 85 return RSVPObject.set_location(self, location + 1, with_builtins) 86 87 def finalise_location(self, with_builtins): 88 self._finalise_location(with_builtins) 89 90 def as_raw(self, objtable, paramtable, with_builtins): 91 item = self.item 92 classcode = objtable.as_list().get_code(item.full_name()) 93 attrcode = objtable.get_index(item.full_name()) 94 95 # Include a template of an instance for use when instantiating classes. 96 97 call_method = item.all_class_attributes().get("__call__") 98 call_method_value = call_method and call_method.get_value() 99 call_method_code_location = call_method_value and call_method_value.code_location 100 call_method_funccode = call_method_value and paramtable.as_list().get_code(call_method_value.full_name()) 101 102 instantiator_funccode = paramtable.as_list().get_code(item.get_instantiator().full_name()) 103 104 # NOTE: The instantiator code is the first block of the class. 105 106 if not self.is_generated(with_builtins): 107 instantiator_code_location = item.full_name() 108 else: 109 instantiator_code_location = item.get_instantiator().blocks[0].location 110 111 return [ 112 113 # Template instance... 114 115 DataObject( 116 classcode, 117 attrcode, # is instance 118 call_method_code_location, 119 item.full_name(), 120 len(item.instance_attributes()) + 1, # size 121 call_method_funccode # funccode 122 ), 123 124 # Class... 125 126 DataObject( 127 classcode, 128 None, # is not instance 129 instantiator_code_location, 130 item.full_name(), 131 len(item.class_attributes()) + 1, # size 132 instantiator_funccode # funccode 133 ) 134 ] 135 136 class RSVPConst(RSVPObject): 137 138 "A wrapper for constants." 139 140 def set_location(self, location, with_builtins): 141 location = RSVPObject.set_location(self, location, with_builtins) 142 return location + len(self.raw_data()) 143 144 def as_raw(self, objtable, paramtable, with_builtins): 145 item = self.item 146 # NOTE: Need class details! 147 return [ 148 DataObject( 149 objtable.as_list().get_code(item.value_type_name()), 150 objtable.get_index(item.value_type_name()), # is instance 151 None, 152 item.value_type_name(), 153 1 # size 154 ) 155 ] + self.raw_data() 156 157 def raw_data(self): 158 item = self.item 159 value = item.get_value() 160 # NOTE: Start simple and use single entries for most types. 161 if item.value_type_name() in ("__builtins__.tuple", "__builtins__.list"): 162 return [len(value)] + list(value) 163 else: 164 return [value] 165 166 class RSVPFunction(RSVPObject): 167 168 "A wrapper for functions." 169 170 def set_location(self, location, with_builtins): 171 item = self.item 172 location = RSVPObject.set_location(self, location, with_builtins) 173 174 # Set the code location only where the code has been 175 # generated. 176 177 if not self.is_generated(with_builtins): 178 item.code_location = item.full_name() 179 180 # Skip any defaults for named functions. 181 182 elif item.name is not None: 183 item.code_location = location + len(item.defaults) 184 185 # Skip any defaults for lambda functions. 186 187 else: 188 item.code_location = location 189 190 return location 191 192 def finalise_location(self, with_builtins): 193 self._finalise_location(with_builtins) 194 195 def as_raw(self, objtable, paramtable, with_builtins): 196 item = self.item 197 # NOTE: Need class and parameter details! Should arguably be an instance of types.FunctionType. 198 return [ 199 DataObject( 200 objtable.as_list().get_code("__builtins__.function"), 201 objtable.get_index("__builtins__.function"), # is instance 202 item.code_location, 203 "__builtins__.function", 204 len(item.defaults) + 1, # size (not accurate for lambda functions before instantiation) 205 paramtable.as_list().get_code(item.full_name()) # funccode 206 ) 207 ] 208 209 class RSVPModule(RSVPObject): 210 211 "A wrapper for modules." 212 213 def as_raw(self, objtable, paramtable, with_builtins): 214 item = self.item 215 return [ 216 DataObject( 217 objtable.as_list().get_code(item.full_name()), 218 None, # modules treated like classes 219 None, 220 item.full_name(), 221 len(item.module_attributes()) + 1 # size 222 ) 223 ] 224 225 # Serialisation-related data and functions. 226 227 def get_object(item): 228 if isinstance(item, Attr): 229 cls = RSVPAttr 230 elif isinstance(item, Block): 231 cls = RSVPBlock 232 elif isinstance(item, Class): 233 cls = RSVPClass 234 elif isinstance(item, Const): 235 cls = RSVPConst 236 elif isinstance(item, Function): 237 cls = RSVPFunction 238 elif isinstance(item, Module): 239 cls = RSVPModule 240 else: 241 cls = None 242 243 if cls is not None: 244 return cls(item) 245 else: 246 return None 247 248 # Instruction-related classes. 249 250 class Instruction: 251 252 "A generic instruction." 253 254 def __init__(self, attr=None): 255 self.attr = attr 256 self.input = None 257 self.source = None # for storage instructions 258 259 def copy(self): 260 return self.__class__(self.attr) 261 262 def __repr__(self): 263 if self.attr is not None: 264 return "%s(%r)%s" % (self.__class__.__name__, self.attr, self.show_input()) 265 else: 266 return "%s()%s" % (self.__class__.__name__, self.show_input()) 267 268 def show_input(self): 269 if self.input is not None: 270 if self.source is not None: 271 return " <- (%r, %r)" % (self.input, self.source) 272 else: 273 return " <- %r" % self.input 274 elif self.source is not None: 275 return " <-- %r" % self.source 276 else: 277 return "" 278 279 def get_operand(self): 280 return None 281 282 class FrameRelativeInstruction(Instruction): 283 284 "An instruction operating on the current frame." 285 286 def __repr__(self): 287 return "%s(%r)%s" % (self.__class__.__name__, self.get_operand(), self.show_input()) 288 289 def get_operand(self): 290 return self.attr.position 291 292 FR = FrameRelativeInstruction 293 294 class AddressRelativeInstruction(Instruction): 295 296 "An instruction accessing an object's attribute." 297 298 def __repr__(self): 299 position = self.get_operand() 300 if position is not None: 301 return "%s(%r)%s # %s" % (self.__class__.__name__, position, self.show_input(), name(self.attr)) 302 else: 303 return "%s(%r)%s" % (self.__class__.__name__, name(self.attr), self.show_input()) 304 305 def get_operand(self): 306 return self.attr.position 307 308 AR = AddressRelativeInstruction 309 310 class AddressInstruction(Instruction): 311 312 "An instruction loading an address directly." 313 314 def __repr__(self): 315 location, position, result = self.get_operands() 316 if location is not None: 317 return "%s(%r)%s # %r, %r (%s)" % ( 318 self.__class__.__name__, result, self.show_input(), location, position, name(self.attr)) 319 elif result is not None: 320 return "%s(%r)%s # %s" % ( 321 self.__class__.__name__, result, self.show_input(), name(self.attr)) 322 else: 323 return "%s(...)%s # %s" % ( 324 self.__class__.__name__, self.show_input(), name(self.attr)) 325 326 def get_operands(self): 327 if isinstance(self.attr, Attr): 328 position = self.attr.position 329 location = self.attr.parent.location 330 331 # NOTE: Unpositioned attributes are handled here. 332 333 if location is not None and position is not None: 334 result = location + position + 1 335 else: 336 location = self.attr.parent.name 337 position = self.attr.name 338 result = None 339 return location, position, result 340 else: 341 return None, None, self.attr.location 342 343 def get_operand(self): 344 return self.get_operands()[-1] 345 346 Address = AddressInstruction 347 348 class TargetInstruction(Instruction): 349 350 "An instruction loading the address of an invocation target." 351 352 def __repr__(self): 353 return "%s(%r) # %r" % (self.__class__.__name__, self.get_operand(), name(self.attr)) 354 355 def get_operand(self): 356 return self.attr.code_body_location 357 358 Target = TargetInstruction 359 360 class ImmediateInstruction(Instruction): 361 362 "An instruction employing a constant." 363 364 def __repr__(self): 365 return "%s(%r)%s" % (self.__class__.__name__, self.attr, self.show_input()) 366 367 def get_operand(self): 368 return self.attr 369 370 Immediate = ImmediateInstruction 371 372 # Access to stored constant data. 373 374 class LoadConst(Address): 375 "Load the constant or module from the specified location." 376 cost = 1 377 378 class LoadClass(Address): 379 "Load the class from the specified location." 380 cost = 1 381 382 class LoadFunction(Address): 383 "Load the function from the specified location." 384 cost = 1 385 386 # Access within an invocation frame. 387 388 class LoadName(FR): 389 "Load the current value from the given local attribute/variable." 390 cost = 2 391 392 class StoreName(FR): 393 "Store the source value into the given local attribute/variable." 394 cost = 2 395 396 class LoadTemp(Immediate): 397 "Load the current value from the given temporary location." 398 cost = 2 399 400 class StoreTemp(Immediate): 401 "Store the current value into the given temporary location." 402 cost = 2 403 404 # Access to static data. 405 406 class LoadAddress(Address): 407 "Load the current value from the given fixed attribute address." 408 cost = 1 409 410 class StoreAddress(Address): 411 "Store the source value into the given fixed attribute address." 412 cost = 1 413 414 class LoadAddressContext(Address): 415 "Load the current value from the given fixed attribute address, using the current value as context." 416 cost = 2 417 418 class StoreAddressContext(Address): 419 "Store the current value into the given fixed attribute address, using the current value as context." 420 cost = 2 421 422 class LoadAddressContextCond(Address): 423 """ 424 Load the current value from the given fixed attribute address, only using the current value as 425 context if the attribute is compatible. 426 """ 427 cost = 4 428 429 class MakeInstance(Immediate): 430 "Make a new instance using the current value as a reference to a template." 431 cost = 5 432 433 class MakeFragment(Immediate): 434 "Make a new list fragment." 435 cost = 5 436 437 # Access to address-relative data. (LoadAttrIndexContext not defined.) 438 439 class LoadAttr(AR): 440 "Load into the current value the given attribute of the object referenced by the current value." 441 cost = 2 442 443 class StoreAttr(AR): 444 "Store the source value into the given attribute of the object referenced by the current value." 445 cost = 2 446 447 class LoadAttrIndex(Immediate): 448 "Load into the current value the attribute of the current value with the given index." 449 cost = 6 450 451 class StoreAttrIndex(Immediate): 452 "Store the source value into the attribute of the current value with the given index." 453 cost = 6 454 455 class LoadAttrIndexContextCond(Immediate): 456 """ 457 Load into the current value the attribute of the current value with the given index, only making the 458 current value the context if the attribute is compatible. 459 """ 460 cost = 8 461 462 # Access to object details. 463 464 class LoadCallable(Instruction): 465 "Load the target of an invocation." 466 cost = 2 467 468 class StoreCallable(Instruction): 469 "Store the source value into the object referenced by the current value." 470 cost = 3 471 472 # Access to invocation frames in preparation. 473 474 class MakeFrame(Immediate): 475 "Make a new invocation frame." 476 cost = 2 477 478 class AdjustFrame(Immediate): 479 "Adjust the current invocation frame for corrected invocations." 480 cost = 2 481 482 class DropFrame(Instruction): 483 "Drop an invocation frame." 484 cost = 2 485 486 class StoreFrame(Immediate): 487 "Store the current value as an argument for the parameter with the given position." 488 cost = 2 489 490 class StoreFrameIndex(Immediate): 491 "Store the source value as an argument of the current value for the parameter with the given index." 492 cost = 6 493 494 class LoadContext(Instruction): 495 "Load the context of an invocation." 496 cost = 2 497 498 # Context-related tests. 499 500 class CheckContext(Instruction): 501 "Check to see if the context is valid." 502 cost = 2 503 504 class CheckClass(Instruction): 505 "Check the current value to determine whether it is a class." 506 cost = 2 507 508 class CheckInstance(Instruction): 509 """ 510 Check the current value as an instance of a class or its subclasses (used with 'self' in an 511 invocation). 512 """ 513 cost = 6 514 515 class CheckType(Instruction): 516 "Check the current value as an instance of a specific class only." 517 cost = 5 518 519 # Access to frames upon invocation. 520 521 class CheckFrame(Immediate): 522 "Check the frame for the correct number of arguments." 523 cost = 3 524 525 class CheckExtra(Immediate): 526 "Ensure that the frame can provide extra arguments." 527 cost = 3 528 529 class FillDefaults(Immediate): 530 "Fill frame positions with defaults, if appropriate." 531 cost = 8 # variable 532 533 class ExtendFrame(Immediate): 534 "Extend the current frame for temporary storage use." 535 cost = 1 536 537 class CopyExtra(Immediate): 538 "Copy extra arguments into a separate sequence, starting from the given position." 539 cost = 10 540 541 # Invocation-related instructions, using a special result "register". 542 543 class JumpInFrame(Instruction): 544 "Jump, using the current locals, to the current callable." 545 cost = 2 546 547 class JumpWithFrame(Instruction): 548 "Jump, adopting the invocation frame, to the current callable." 549 cost = 3 550 551 class JumpWithFrameDirect(Target): 552 "Jump to the specified address, adopting the invocation frame." 553 cost = 3 554 555 class Return(Instruction): 556 "Return from a subprogram." 557 cost = 2 558 559 class LoadResult(Instruction): 560 "Load into the current value a returned value." 561 cost = 1 562 563 class StoreResult(Instruction): 564 "Store the current value as a value to be returned." 565 cost = 1 566 567 # Branch-related instructions. 568 569 class Jump(Address): 570 "Jump unconditionally." 571 cost = 1 572 573 class JumpIfFalse(Address): 574 "Jump if the last evaluation gave a false result." 575 cost = 2 576 577 class JumpIfTrue(Address): 578 "Jump if the last evaluation gave a true result." 579 cost = 2 580 581 # Exception-related instructions, using a special exception "register". 582 583 class LoadException(Instruction): 584 "Load the raised exception." 585 cost = 1 586 587 class StoreException(Instruction): 588 "Store the current object in the exception register." 589 cost = 1 590 591 class ClearException(Instruction): 592 "Reset the exception register." 593 cost = 1 594 595 class RaiseException(Instruction): 596 "Raise an exception, jumping to the active handler." 597 cost = 2 598 599 class PushHandler(Address): 600 "Push an exception handler onto the handler stack." 601 cost = 3 602 603 class PopHandler(Instruction): 604 "Pop an exception handler from the handler stack." 605 cost = 3 606 607 class CheckException(Instruction): 608 "Check the raised exception against another." 609 cost = 6 610 611 # Test instructions, operating on the boolean status register. 612 613 class TestIdentity(Instruction): 614 "Test whether the current value is identical to the source value, setting the boolean status." 615 cost = 2 616 617 class TestIdentityAddress(Address): 618 "Test whether the current value is identical to the given address, setting the boolean status." 619 cost = 2 620 621 class InvertBoolean(Instruction): 622 "Invert the boolean status." 623 cost = 1 624 625 # Instructions which affect the current value. (LoadAttrIndexContext not defined.) 626 627 current_value_instructions = ( 628 LoadConst, LoadClass, LoadFunction, LoadName, LoadTemp, 629 LoadAddress, LoadAddressContext, LoadAddressContextCond, 630 LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond, 631 LoadCallable, LoadContext, LoadResult, 632 LoadException, MakeInstance, MakeFragment, 633 CopyExtra 634 ) 635 636 # Instructions which use the current value. (LoadAttrIndexContext not defined.) 637 638 simple_input_user_instructions = ( 639 StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored 640 LoadAddressContext, LoadAddressContextCond, # as the object being referenced 641 LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond, # as the object being referenced 642 StoreAttr, StoreAttrIndex, StoreCallable, # as the object being referenced 643 StoreFrameIndex, # as the object being referenced 644 StoreAddressContext, # as the context 645 LoadCallable, 646 TestIdentity, TestIdentityAddress, CheckInstance, # as one of the operands 647 CheckException, CheckFrame, FillDefaults, 648 MakeInstance, 649 CheckContext, CheckClass, CheckType, 650 LoadContext # as the object providing the result 651 ) 652 653 # vim: tabstop=4 expandtab shiftwidth=4