1 #!/usr/bin/env python 2 3 """ 4 RSVP instruction and serialisation classes. 5 6 Copyright (C) 2007, 2008, 2009 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 25 def name(attr): 26 if isinstance(attr, Attr): 27 return attr.name or "<unnamed>" 28 elif isinstance(attr, (Block, Const)): 29 return attr 30 else: 31 return attr.full_name() or "<unnamed>" 32 33 # Serialisation-related classes. 34 35 class RSVPObject: 36 37 "A generic data object wrapper." 38 39 def __init__(self, item): 40 self.item = item 41 42 def set_location(self, location, with_builtins): 43 self.item.location = location 44 return location + 1 45 46 def finalise_location(self, with_builtins): 47 pass 48 49 def _finalise_location(self, with_builtins): 50 51 """ 52 Set the code body location for items now that the code blocks have been 53 positioned. 54 """ 55 56 item = self.item 57 58 if not with_builtins and item.module.name == "__builtins__" and item.astnode.doc is None: 59 item.code_body_location = item.full_name() 60 else: 61 item.code_body_location = item.get_body_block().location 62 63 class RSVPAttr(RSVPObject): 64 65 "A wrapper for attributes/values." 66 67 def as_raw(self, objtable, paramtable, with_builtins): 68 item = self.item 69 return [ 70 DataValue( 71 item.get_context() and item.get_context().location, 72 item.get_value() and item.get_value().location 73 ) 74 ] 75 76 class RSVPBlock(RSVPObject): 77 78 "A wrapper for blocks." 79 80 def set_location(self, location, with_builtins): 81 item = self.item 82 item.location = location 83 return location + len(item.code) 84 85 def as_raw(self, objtable, paramtable, with_builtins): 86 return self.item.code 87 88 class RSVPClass(RSVPObject): 89 90 "A wrapper for classes." 91 92 def set_location(self, location, with_builtins): 93 self.item.instance_template_location = location 94 return RSVPObject.set_location(self, location + 1, with_builtins) 95 96 def finalise_location(self, with_builtins): 97 self._finalise_location(with_builtins) 98 99 def as_raw(self, objtable, paramtable, with_builtins): 100 item = self.item 101 classcode = objtable.as_list().get_code(item.full_name()) 102 attrcode = objtable.get_index(item.full_name()) 103 104 # Include a template of an instance for use when instantiating classes. 105 106 call_method = item.all_class_attributes().get("__call__") 107 call_method_value = call_method and call_method.get_value() 108 call_method_code_location = call_method_value and call_method_value.code_location 109 call_method_funccode = call_method_value and paramtable.as_list().get_code(call_method_value.full_name()) 110 111 instantiator_funccode = paramtable.as_list().get_code(item.get_instantiator().full_name()) 112 113 # NOTE: The instantiator code is the first block of the class. 114 115 with_instantiator = with_builtins or item.module.name != "__builtins__" or item.astnode.doc is not None 116 117 if not with_instantiator: 118 instantiator_code_location = item.full_name() 119 else: 120 instantiator_code_location = item.get_instantiator().blocks[0].location 121 122 return [ 123 124 # Template instance... 125 126 DataObject( 127 classcode, 128 attrcode, # is instance 129 call_method_code_location, 130 item.full_name(), 131 len(item.instance_attributes()) + 1, # size 132 call_method_funccode # funccode 133 ), 134 135 # Class... 136 137 DataObject( 138 classcode, 139 None, # is not instance 140 instantiator_code_location, 141 item.full_name(), 142 len(item.class_attributes()) + 1, # size 143 instantiator_funccode # funccode 144 ) 145 ] 146 147 class RSVPConst(RSVPObject): 148 149 "A wrapper for constants." 150 151 def set_location(self, location, with_builtins): 152 location = RSVPObject.set_location(self, location, with_builtins) 153 return location + len(self.raw_data()) 154 155 def as_raw(self, objtable, paramtable, with_builtins): 156 item = self.item 157 # NOTE: Need class details! 158 return [ 159 DataObject( 160 objtable.as_list().get_code(item.value_type_name()), 161 objtable.get_index(item.value_type_name()), # is instance 162 None, 163 item.value_type_name(), 164 1 # size 165 ) 166 ] + self.raw_data() 167 168 def raw_data(self): 169 item = self.item 170 value = item.get_value() 171 # NOTE: Start simple and use single entries for most types. 172 if item.value_type_name() in ("__builtins__.tuple", "__builtins__.list"): 173 return [len(value)] + list(value) 174 else: 175 return [value] 176 177 class RSVPFunction(RSVPObject): 178 179 "A wrapper for functions." 180 181 def set_location(self, location, with_builtins): 182 item = self.item 183 location = RSVPObject.set_location(self, location, with_builtins) 184 185 # Set the code location only where the code has been 186 # generated. 187 188 if not with_builtins and item.module.name == "__builtins__" and item.astnode.doc is None: 189 item.code_location = item.full_name() 190 191 # Skip any defaults for named functions. 192 193 elif item.name is not None: 194 item.code_location = location + len(item.defaults) 195 196 # Skip any defaults for lambda functions. 197 198 else: 199 item.code_location = location 200 201 return location 202 203 def finalise_location(self, with_builtins): 204 self._finalise_location(with_builtins) 205 206 def as_raw(self, objtable, paramtable, with_builtins): 207 item = self.item 208 # NOTE: Need class and parameter details! Should arguably be an instance of types.FunctionType. 209 return [ 210 DataObject( 211 objtable.as_list().get_code("__builtins__.function"), 212 objtable.get_index("__builtins__.function"), # is instance 213 item.code_location, 214 "__builtins__.function", 215 len(item.defaults) + 1, # size (not accurate for lambda functions before instantiation) 216 paramtable.as_list().get_code(item.full_name()) # funccode 217 ) 218 ] 219 220 class RSVPModule(RSVPObject): 221 222 "A wrapper for modules." 223 224 def as_raw(self, objtable, paramtable, with_builtins): 225 item = self.item 226 return [ 227 DataObject( 228 objtable.as_list().get_code(item.full_name()), 229 None, # modules treated like classes 230 None, 231 item.full_name(), 232 len(item.module_attributes()) + 1 # size 233 ) 234 ] 235 236 # Serialisation-related data and functions. 237 238 def get_object(item): 239 if isinstance(item, Attr): 240 cls = RSVPAttr 241 elif isinstance(item, Block): 242 cls = RSVPBlock 243 elif isinstance(item, Class): 244 cls = RSVPClass 245 elif isinstance(item, Const): 246 cls = RSVPConst 247 elif isinstance(item, Function): 248 cls = RSVPFunction 249 elif isinstance(item, Module): 250 cls = RSVPModule 251 else: 252 cls = None 253 254 if cls is not None: 255 return cls(item) 256 else: 257 return None 258 259 # Instruction-related classes. 260 261 class Instruction: 262 263 "A generic instruction." 264 265 def __init__(self, attr=None): 266 self.attr = attr 267 self.input = None 268 self.source = None # for storage instructions 269 270 def copy(self): 271 return self.__class__(self.attr) 272 273 def __repr__(self): 274 if self.attr is not None: 275 return "%s(%r)%s" % (self.__class__.__name__, self.attr, self.show_input()) 276 else: 277 return "%s()%s" % (self.__class__.__name__, self.show_input()) 278 279 def show_input(self): 280 if self.input is not None: 281 if self.source is not None: 282 return " <- (%r, %r)" % (self.input, self.source) 283 else: 284 return " <- %r" % self.input 285 elif self.source is not None: 286 return " <-- %r" % self.source 287 else: 288 return "" 289 290 def get_operand(self): 291 return None 292 293 class FrameRelativeInstruction(Instruction): 294 295 "An instruction operating on the current frame." 296 297 def __repr__(self): 298 return "%s(%r)%s" % (self.__class__.__name__, self.get_operand(), self.show_input()) 299 300 def get_operand(self): 301 return self.attr.position 302 303 FR = FrameRelativeInstruction 304 305 class AddressRelativeInstruction(Instruction): 306 307 "An instruction accessing an object's attribute." 308 309 def __repr__(self): 310 position = self.get_operand() 311 if position is not None: 312 return "%s(%r)%s # %s" % (self.__class__.__name__, position, self.show_input(), name(self.attr)) 313 else: 314 return "%s(%r)%s" % (self.__class__.__name__, name(self.attr), self.show_input()) 315 316 def get_operand(self): 317 return self.attr.position 318 319 AR = AddressRelativeInstruction 320 321 class AddressInstruction(Instruction): 322 323 "An instruction loading an address directly." 324 325 def __repr__(self): 326 location, position, result = self.get_operands() 327 if location is not None: 328 return "%s(%r)%s # %r, %r (%s)" % ( 329 self.__class__.__name__, result, self.show_input(), location, position, name(self.attr)) 330 elif result is not None: 331 return "%s(%r)%s # %s" % ( 332 self.__class__.__name__, result, self.show_input(), name(self.attr)) 333 else: 334 return "%s(...)%s # %s" % ( 335 self.__class__.__name__, self.show_input(), name(self.attr)) 336 337 def get_operands(self): 338 if isinstance(self.attr, Attr): 339 position = self.attr.position 340 location = self.attr.parent.location 341 342 # NOTE: Unpositioned attributes are handled here. 343 344 if location is not None and position is not None: 345 result = location + position + 1 346 else: 347 location = self.attr.parent.name 348 position = self.attr.name 349 result = None 350 return location, position, result 351 else: 352 return None, None, self.attr.location 353 354 def get_operand(self): 355 return self.get_operands()[-1] 356 357 Address = AddressInstruction 358 359 class TargetInstruction(Instruction): 360 361 "An instruction loading the address of an invocation target." 362 363 def __repr__(self): 364 return "%s(%r) # %r" % (self.__class__.__name__, self.get_operand(), name(self.attr)) 365 366 def get_operand(self): 367 return self.attr.code_body_location 368 369 Target = TargetInstruction 370 371 class ImmediateInstruction(Instruction): 372 373 "An instruction employing a constant." 374 375 def __repr__(self): 376 return "%s(%r)%s" % (self.__class__.__name__, self.attr, self.show_input()) 377 378 def get_operand(self): 379 return self.attr 380 381 Immediate = ImmediateInstruction 382 383 # Access to stored constant data. 384 385 class LoadConst(Address): "Load the constant or module from the specified location." 386 class LoadClass(Address): "Load the class from the specified location." 387 class LoadFunction(Address): "Load the function from the specified location." 388 389 # Access within an invocation frame. 390 391 class LoadName(FR): "Load the current value from the given local attribute/variable." 392 class StoreName(FR): "Store the source value into the given local attribute/variable." 393 class LoadTemp(Immediate): "Load the current value from the given temporary location." 394 class StoreTemp(Immediate): "Store the current value into the given temporary location." 395 396 # Access to static data. 397 398 class LoadAddress(Address): "Load the current value from the given fixed attribute address." 399 class StoreAddress(Address): "Store the source value into the given fixed attribute address." 400 class LoadAddressContext(Address): "Load the current value from the given fixed attribute address, using the current value as context." 401 class StoreAddressContext(Address): "Store the current value into the given fixed attribute address, using the current value as context." 402 class LoadAddressContextCond(Address): 403 """Load the current value from the given fixed attribute address, only using the current value as 404 context if the attribute is compatible.""" 405 class MakeInstance(Immediate): "Make a new instance using the current value as a reference to a template." 406 class MakeFragment(Immediate): "Make a new list fragment." 407 408 # Access to address-relative data. (LoadAttrIndexContext not defined.) 409 410 class LoadAttr(AR): "Load into the current value the given attribute of the object referenced by the current value." 411 class StoreAttr(AR): "Store the source value into the given attribute of the object referenced by the current value." 412 class LoadAttrIndex(Immediate): "Load into the current value the attribute of the current value with the given index." 413 class StoreAttrIndex(Immediate): "Store the source value into the attribute of the current value with the given index." 414 class LoadAttrIndexContextCond(Immediate): 415 """Load into the current value the attribute of the current value with the given index, only making the 416 current value the context if the attribute is compatible.""" 417 418 # Access to object details. 419 420 class LoadCallable(Instruction): "Load the target of an invocation." 421 class StoreCallable(Instruction): "Store the source value into the object referenced by the current value." 422 423 # Access to invocation frames in preparation. 424 425 class MakeFrame(Immediate): "Make a new invocation frame." 426 class AdjustFrame(Immediate): "Adjust the current invocation frame for corrected invocations." 427 class DropFrame(Instruction): "Drop an invocation frame." 428 class StoreFrame(Immediate): "Store the current value as an argument for the parameter with the given position." 429 class StoreFrameIndex(Immediate): "Store the source value as an argument of the current value for the parameter with the given index." 430 class LoadContext(Instruction): "Load the context of an invocation." 431 432 # Context-related tests. 433 434 class CheckContext(Instruction): "Check to see if the context is valid." 435 class CheckClass(Instruction): "Check the current value to determine whether it is a class." 436 class CheckInstance(Instruction): "Check the current value as an instance of a specific class (used with 'self' in an invocation)." 437 438 # Access to frames upon invocation. 439 440 class CheckFrame(Immediate): "Check the frame for the correct number of arguments." 441 class CheckExtra(Immediate): "Ensure that the frame can provide extra arguments." 442 class FillDefaults(Immediate): "Fill frame positions with defaults, if appropriate." 443 class ExtendFrame(Immediate): "Extend the current frame for temporary storage use." 444 class CopyExtra(Immediate): "Copy extra arguments into a separate sequence, starting from the given position." 445 446 # Invocation-related instructions, using a special result "register". 447 448 class JumpInFrame(Instruction): "Jump, using the current locals, to the current callable." 449 class JumpWithFrame(Instruction): "Jump, adopting the invocation frame, to the current callable." 450 class JumpWithFrameDirect(Target): "Jump to the specified address, adopting the invocation frame." 451 class Return(Instruction): "Return from a subprogram." 452 class LoadResult(Instruction): "Load into the current value a returned value." 453 class StoreResult(Instruction): "Store the current value as a value to be returned." 454 455 # Branch-related instructions. 456 457 class Jump(Address): "Jump unconditionally." 458 class JumpIfFalse(Address): "Jump if the last evaluation gave a false result." 459 class JumpIfTrue(Address): "Jump if the last evaluation gave a true result." 460 461 # Exception-related instructions, using a special exception "register". 462 463 class LoadException(Instruction): "Load the raised exception." 464 class StoreException(Instruction): "Store the current object in the exception register." 465 class ClearException(Instruction): "Reset the exception register." 466 class RaiseException(Instruction): "Raise an exception, jumping to the active handler." 467 class PushHandler(Address): "Push an exception handler onto the handler stack." 468 class PopHandler(Instruction): "Pop an exception handler from the handler stack." 469 class CheckException(Instruction): "Check the raised exception against another." 470 471 # Test instructions, operating on the boolean status register. 472 473 class TestIdentity(Instruction): "Test whether the current value is identical to the source value, setting the boolean status." 474 class TestIdentityAddress(Address): "Test whether the current value is identical to the given address, setting the boolean status." 475 class InvertBoolean(Instruction): "Invert the boolean status." 476 477 # Instructions which affect the current value. (LoadAttrIndexContext not defined.) 478 479 current_value_instructions = ( 480 LoadConst, LoadClass, LoadFunction, LoadName, LoadTemp, 481 LoadAddress, LoadAddressContext, LoadAddressContextCond, 482 LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond, 483 LoadCallable, LoadContext, LoadResult, 484 LoadException, MakeInstance, MakeFragment, 485 CopyExtra 486 ) 487 488 # Instructions which use the current value. 489 490 simple_input_user_instructions = ( 491 StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored 492 LoadAddressContext, LoadAddressContextCond, # as the object being referenced 493 LoadAttr, LoadAttrIndex, # LoadAttrIndexContext, # as the object being referenced 494 LoadAttrIndexContextCond, # as the object being referenced 495 StoreAttr, StoreAttrIndex, StoreCallable, # as the object being referenced 496 StoreFrameIndex, # as the object being referenced 497 StoreAddressContext, # as the context 498 LoadCallable, 499 TestIdentity, TestIdentityAddress, CheckInstance, # as one of the operands 500 CheckException, CheckFrame, FillDefaults, 501 MakeInstance, 502 CheckContext, CheckClass, 503 LoadContext # as the object providing the result 504 ) 505 506 # vim: tabstop=4 expandtab shiftwidth=4