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