1 #!/usr/bin/env python 2 3 """ 4 A native function library for a really simple virtual processor. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011 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.program import DataValue 23 import operator 24 25 class Library: 26 27 "Native function implementations." 28 29 # NOTE: These attributes need changing if the instance layout changes. 30 31 instance_template_size = instance_data_offset = 1 32 instance_size = instance_template_size + 1 33 fragment_data_offset = 1 34 35 def __init__(self, machine, constants): 36 37 """ 38 Initialise the library with the 'machine' and the 'constants' addresses 39 dictionary. 40 """ 41 42 self.machine = machine 43 self.constants = constants 44 45 # Native class constants. 46 47 self.int_class, self.int_instance = self._get_builtin_class_and_template("int") 48 self.list_class, self.list_instance = self._get_builtin_class_and_template("list") 49 self.index_error, self.index_error_instance = self._get_builtin_class_and_template("IndexError") 50 self.str_class, self.str_instance = self._get_builtin_class_and_template("basestring") 51 self.accessor_class, self.accessor_instance = self._get_builtin_class_and_template("_accessor") 52 53 self.tuple_class = self.machine.tuple_class 54 self.tuple_instance = self.machine.tuple_instance 55 56 self.attr_error_instance = self.machine.attr_error_instance 57 self.type_error_instance = self.machine.type_error_instance 58 59 self.frame_stack = self.machine.frame_stack 60 self.local_sp_stack = self.machine.local_sp_stack 61 62 def _get_builtin_class_and_template(self, name): 63 cls = self.machine._get_class("__builtins__", name) 64 if cls is not None: 65 return cls.location, cls.instance_template_location 66 else: 67 return None, None 68 69 def _check_index(self, pos, nelements): 70 return pos >= 0 and pos < nelements 71 72 def builtins_int_arithmetic_op(self, op): 73 frame = self.local_sp_stack[-1] 74 75 # Get operands addresses. 76 77 left_value = self.frame_stack[frame] 78 right_value = self.frame_stack[frame + 1] 79 80 # Test operand suitability. 81 # NOTE: Support other types. 82 83 if not (self.machine._CheckInstance(left_value.ref, self.int_class) and 84 self.machine._CheckInstance(right_value.ref, self.int_class)): 85 86 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 87 return self.machine.RaiseException() 88 89 left_data = left_value.ref + self.instance_data_offset 90 right_data = right_value.ref + self.instance_data_offset 91 92 # Make a new object. 93 94 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 95 96 # Store the result. 97 # NOTE: The data is considered ready to use. 98 99 self.machine.save(addr + self.instance_data_offset, op(self.machine.load(left_data), self.machine.load(right_data))) 100 101 # Return the new object. 102 # Introduce object as context for the new object. 103 104 self.machine.result_context = addr 105 self.machine.result_value = addr 106 107 def builtins_logical_op(self, operand_class, op): 108 frame = self.local_sp_stack[-1] 109 110 # Get operands addresses. 111 112 left_value = self.frame_stack[frame] 113 right_value = self.frame_stack[frame + 1] 114 115 # Test operand suitability. 116 # NOTE: Handle comparisons of incompatible types more appropriately. 117 # NOTE: Return NotImplemented. 118 119 if not (self.machine._CheckInstance(left_value.ref, operand_class) and 120 self.machine._CheckInstance(right_value.ref, operand_class)): 121 122 notimpl = self.constants[NotImplemented] 123 self.machine.result_context = notimpl 124 self.machine.result_value = notimpl 125 return 126 127 left_data = left_value.ref + self.instance_data_offset 128 right_data = right_value.ref + self.instance_data_offset 129 130 # Test the data. 131 # NOTE: The data is considered ready to use. 132 133 if op(self.machine.load(left_data), self.machine.load(right_data)): 134 self.machine.result_context = self.constants[True] 135 self.machine.result_value = self.constants[True] 136 else: 137 self.machine.result_context = self.constants[False] 138 self.machine.result_value = self.constants[False] 139 140 # Operators. 141 # Although this takes a short-cut by using the operator module, testing is 142 # still performed on the operands to ensure that they qualify for these 143 # native operations. 144 145 def builtins_int_add(self): 146 return self.builtins_int_arithmetic_op(operator.add) 147 148 def builtins_int_sub(self): 149 return self.builtins_int_arithmetic_op(operator.sub) 150 151 def builtins_int_pow(self): 152 return self.builtins_int_arithmetic_op(operator.pow) 153 154 def builtins_int_lt(self): 155 return self.builtins_logical_op(self.int_class, operator.lt) 156 157 def builtins_int_le(self): 158 return self.builtins_logical_op(self.int_class, operator.le) 159 160 def builtins_int_gt(self): 161 return self.builtins_logical_op(self.int_class, operator.gt) 162 163 def builtins_int_ge(self): 164 return self.builtins_logical_op(self.int_class, operator.ge) 165 166 def builtins_int_eq(self): 167 return self.builtins_logical_op(self.int_class, operator.eq) 168 169 def builtins_int_ne(self): 170 return self.builtins_logical_op(self.int_class, operator.ne) 171 172 def builtins_str_lt(self): 173 return self.builtins_logical_op(self.str_class, operator.lt) 174 175 def builtins_str_le(self): 176 return self.builtins_logical_op(self.str_class, operator.le) 177 178 def builtins_str_gt(self): 179 return self.builtins_logical_op(self.str_class, operator.gt) 180 181 def builtins_str_ge(self): 182 return self.builtins_logical_op(self.str_class, operator.ge) 183 184 def builtins_str_eq(self): 185 return self.builtins_logical_op(self.str_class, operator.eq) 186 187 def builtins_str_ne(self): 188 return self.builtins_logical_op(self.str_class, operator.ne) 189 190 def builtins_int_and(self): 191 return self.builtins_int_arithmetic_op(operator.and_) 192 193 def builtins_int_or(self): 194 return self.builtins_int_arithmetic_op(operator.or_) 195 196 # Specific operator methods. 197 198 def builtins_int_bool(self): 199 frame = self.local_sp_stack[-1] 200 201 # Get operands addresses. 202 203 left_value = self.frame_stack[frame] 204 205 # Test operand suitability. 206 207 if not self.machine._CheckInstance(left_value.ref, self.int_class): 208 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 209 return self.machine.RaiseException() 210 211 left_data = left_value.ref + self.instance_data_offset 212 213 # Test the data. 214 # NOTE: The data is considered ready to use. 215 216 if self.machine.load(left_data) != 0: 217 self.machine.result_context = self.constants[True] 218 self.machine.result_value = self.constants[True] 219 else: 220 self.machine.result_context = self.constants[False] 221 self.machine.result_value = self.constants[False] 222 223 def builtins_int_neg(self): 224 frame = self.local_sp_stack[-1] 225 226 # Get operands addresses. 227 228 left_value = self.frame_stack[frame] 229 230 # Test operand suitability. 231 232 if not self.machine._CheckInstance(left_value.ref, self.int_class): 233 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 234 return self.machine.RaiseException() 235 236 left_data = left_value.ref + self.instance_data_offset 237 238 # Make a new object. 239 240 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 241 242 # Store the result. 243 # NOTE: The data is considered ready to use. 244 245 self.machine.save(addr + self.instance_data_offset, -self.machine.load(left_data)) 246 247 # Return the new object. 248 # Introduce object as context for the new object. 249 250 self.machine.result_context = addr 251 self.machine.result_value = addr 252 253 # Various built-in methods. 254 255 def builtins_bool_bool(self): 256 frame = self.local_sp_stack[-1] 257 258 # Get operands addresses. 259 260 left_value = self.frame_stack[frame] 261 self.machine.result_context = left_value.ref 262 self.machine.result_value = left_value.ref 263 264 def builtins_list_new(self): 265 frame = self.local_sp_stack[-1] 266 267 # The first parameter should be the instance. 268 269 list_value = self.frame_stack[frame] 270 271 # Make a new sequence. 272 # NOTE: Using an arbitrary size. 273 274 new_fragment = self.machine._MakeFragment(self.fragment_data_offset, 5) # include the header 275 276 # Complete the list instance by saving the fragment reference. 277 # NOTE: This requires an attribute in the list structure. 278 279 addr = list_value.ref + self.instance_data_offset 280 self.machine.save(addr, DataValue(None, new_fragment)) 281 282 def builtins_list_get_single_item(self): 283 frame = self.local_sp_stack[-1] 284 285 # Get the operand address. 286 287 item_value = self.frame_stack[frame + 1] 288 289 # Get the list address. 290 291 obj_value = self.frame_stack[frame] 292 293 # Get the fragment address. 294 295 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 296 297 # Get the fragment header. 298 299 header = self.machine.load(fragment.ref) 300 nelements = header.occupied_size - self.fragment_data_offset 301 302 # Get the item position. 303 304 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 305 306 if not self._check_index(item_pos, nelements): 307 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 308 return self.machine.RaiseException() 309 310 # Get the item itself. 311 312 data = self.machine.load(fragment.ref + self.fragment_data_offset + item_pos) 313 self.machine.result_context = data.context 314 self.machine.result_value = data.ref 315 316 def builtins_list_len(self): 317 frame = self.local_sp_stack[-1] 318 319 # Get the list address. 320 321 obj_value = self.frame_stack[frame] 322 323 # Get the fragment address. 324 325 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 326 327 # Get the fragment header. 328 329 header = self.machine.load(fragment.ref) 330 nelements = header.occupied_size - self.fragment_data_offset 331 332 # Make a new object. 333 334 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 335 336 # Store the result. 337 # NOTE: The data is considered ready to use. 338 339 self.machine.save(addr + self.instance_data_offset, nelements) 340 341 # Return the new object. 342 # Introduce object as context for the new object. 343 344 self.machine.result_context = addr 345 self.machine.result_value = addr 346 347 def builtins_list_append(self): 348 frame = self.local_sp_stack[-1] 349 350 # Get operand address. 351 352 arg_value = self.frame_stack[frame + 1] 353 354 # Get the list address. 355 356 obj_value = self.frame_stack[frame] 357 358 # Get the fragment address. 359 360 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 361 362 # Get the fragment header. 363 364 header = self.machine.load(fragment.ref) 365 366 # Attempt to add the reference. 367 368 if header.occupied_size < header.allocated_size: 369 self.machine.save(fragment.ref + header.occupied_size, arg_value) 370 header.occupied_size += 1 371 else: 372 373 # Make a new fragment, maintaining more space than currently 374 # occupied in order to avoid reallocation. 375 376 new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2) 377 378 # Copy existing elements. 379 380 for i in range(self.fragment_data_offset, header.occupied_size): 381 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i)) 382 383 self.machine.save(new_fragment + header.occupied_size, arg_value) 384 385 # Set the new fragment in the object. 386 # NOTE: The old fragment could be deallocated. 387 388 self.machine.save(obj_value.ref + self.instance_data_offset, DataValue(None, new_fragment)) 389 390 def builtins_tuple_new(self): 391 frame = self.local_sp_stack[-1] 392 393 # Get the sequence address. 394 # The first argument should be empty since this function acts as an 395 # instantiator, and in instantiators the first argument is reserved so 396 # that it can be filled in for the call to an initialiser without 397 # allocating a new frame. 398 399 obj_value = self.frame_stack[frame + 1] 400 return self._builtins_tuple(obj_value) 401 402 def builtins_tuple(self): 403 frame = self.local_sp_stack[-1] 404 405 # Get the sequence address. 406 407 obj_value = self.frame_stack[frame] 408 return self._builtins_tuple(obj_value) 409 410 def _builtins_tuple(self, obj_value): 411 412 if self.machine._CheckInstance(obj_value.ref, self.tuple_class): 413 self.machine.result_context = obj_value.context 414 self.machine.result_value = obj_value.ref 415 return 416 417 # Reject non-list, non-tuple types. 418 # NOTE: This should probably accept any sequence. 419 420 elif not self.machine._CheckInstance(obj_value.ref, self.list_class): 421 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 422 return self.machine.RaiseException() 423 424 # Get the fragment address. 425 426 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 427 428 # Get the fragment header. 429 430 header = self.machine.load(fragment.ref) 431 432 # Make a new object. 433 434 addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance) 435 436 # Copy the fragment contents into the tuple. 437 # NOTE: This might be done by repurposing the fragment in some situations. 438 439 for i in range(self.fragment_data_offset, header.occupied_size): 440 self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i)) 441 442 # Return the new object. 443 # Introduce object as context for the new object. 444 445 self.machine.result_context = addr 446 self.machine.result_value = addr 447 448 def builtins_tuple_len(self): 449 frame = self.local_sp_stack[-1] 450 451 # Get the tuple address. 452 453 obj_value = self.frame_stack[frame] 454 455 # Get the header. 456 457 header = self.machine.load(obj_value.ref) 458 nelements = header.size - self.instance_data_offset 459 460 # Make a new object. 461 462 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 463 464 # Store the result. 465 # NOTE: The data is considered ready to use. 466 467 self.machine.save(addr + self.instance_data_offset, nelements) 468 469 # Return the new object. 470 # Introduce object as context for the new object. 471 472 self.machine.result_context = addr 473 self.machine.result_value = addr 474 475 def builtins_tuple_get_single_item(self): 476 frame = self.local_sp_stack[-1] 477 478 # Get the operand address. 479 480 item_value = self.frame_stack[frame + 1] 481 482 # Get the tuple address. 483 484 obj_value = self.frame_stack[frame] 485 486 # Get the header. 487 488 header = self.machine.load(obj_value.ref) 489 nelements = header.size - self.instance_data_offset 490 491 # NOTE: Assume single location for data and header. 492 493 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 494 495 if not self._check_index(item_pos, nelements): 496 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 497 return self.machine.RaiseException() 498 499 # Get the item. 500 501 data = self.machine.load(obj_value.ref + self.instance_data_offset + item_pos) 502 self.machine.result_context = data.context 503 self.machine.result_value = data.ref 504 505 def builtins_object_init(self): 506 pass 507 508 def builtins_getattr(self): 509 frame = self.local_sp_stack[-1] 510 511 # Get the object, attribute name. 512 513 obj_value = self.frame_stack[frame] 514 name_value = self.frame_stack[frame + 1] 515 516 if not self.machine._CheckInstance(name_value.ref, self.accessor_class): 517 self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance) 518 return self.machine.RaiseException() 519 520 # Get the object table index from the name. It is a bare integer, not a reference. 521 522 index = self.machine.load(name_value.ref + self.instance_data_offset + 1) 523 524 # NOTE: This is very much like LoadAttrIndexContextCond. 525 526 data = self.machine.load(obj_value.ref) 527 element = self.machine.objlist[data.classcode + index] 528 529 if element is not None: 530 attr_index, static_attr, offset = element 531 if attr_index == index: 532 if static_attr: 533 loaded_data = self.machine.load(offset) # offset is address of class/module attribute 534 if data.attrcode is not None: # absent attrcode == class/module 535 loaded_data = self.machine._LoadAddressContextCond(loaded_data.context, loaded_data.ref, obj_value.ref) 536 else: 537 loaded_data = self.machine.load(obj_value.ref + offset) 538 self.machine.result_context = loaded_data.context 539 self.machine.result_value = loaded_data.ref 540 return 541 542 self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance) 543 return self.machine.RaiseException() 544 545 def builtins_isinstance(self): 546 frame = self.local_sp_stack[-1] 547 548 # Get the operand addresses. 549 550 obj_value = self.frame_stack[frame] 551 cls_value = self.frame_stack[frame + 1] 552 553 if self.machine._CheckInstance(obj_value.ref, cls_value.ref): 554 self.machine.result_context = self.constants[True] 555 self.machine.result_value = self.constants[True] 556 else: 557 self.machine.result_context = self.constants[False] 558 self.machine.result_value = self.constants[False] 559 560 def builtins_print(self): 561 # NOTE: Do nothing for now. 562 pass 563 564 def builtins_printnl(self): 565 # NOTE: Do nothing for now. 566 pass 567 568 native_functions = { 569 570 # Native method implementations: 571 572 "__builtins__.basestring.__lt__" : builtins_str_lt, 573 "__builtins__.basestring.__le__" : builtins_str_le, 574 "__builtins__.basestring.__gt__" : builtins_str_gt, 575 "__builtins__.basestring.__ge__" : builtins_str_ge, 576 "__builtins__.basestring.__eq__" : builtins_str_eq, 577 "__builtins__.basestring.__ne__" : builtins_str_ne, 578 "__builtins__.bool.__bool__" : builtins_bool_bool, 579 "__builtins__.int.__add__" : builtins_int_add, 580 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 581 "__builtins__.int.__sub__" : builtins_int_sub, 582 "__builtins__.int.__pow__" : builtins_int_pow, 583 "__builtins__.int.__iadd__" : builtins_int_add, 584 "__builtins__.int.__bool__" : builtins_int_bool, 585 "__builtins__.int.__neg__" : builtins_int_neg, 586 "__builtins__.int.__lt__" : builtins_int_lt, 587 "__builtins__.int.__le__" : builtins_int_le, 588 "__builtins__.int.__gt__" : builtins_int_gt, 589 "__builtins__.int.__ge__" : builtins_int_ge, 590 "__builtins__.int.__eq__" : builtins_int_eq, 591 "__builtins__.int.__ne__" : builtins_int_ne, 592 "__builtins__.int.__and__" : builtins_int_and, 593 "__builtins__.int.__rand__" : builtins_int_and, 594 "__builtins__.int.__or__" : builtins_int_or, 595 "__builtins__.int.__ror__" : builtins_int_or, 596 "__builtins__.list.__get_single_item__" : builtins_list_get_single_item, 597 "__builtins__.list.__len__" : builtins_list_len, 598 "__builtins__.list.append" : builtins_list_append, 599 "__builtins__.tuple" : builtins_tuple_new, 600 "__builtins__.tuple.__len__" : builtins_tuple_len, 601 "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item, 602 603 # Native initialisers: 604 605 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 606 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 607 608 # Native functions: 609 610 "__builtins__._getattr" : builtins_getattr, 611 612 # Native instantiator helpers: 613 614 "__builtins__.list.__new__" : builtins_list_new, 615 616 # Native helper functions: 617 618 "__builtins__._isinstance" : builtins_isinstance, 619 "__builtins__._print" : builtins_print, 620 "__builtins__._printnl" : builtins_printnl, 621 "__builtins__._tuple" : builtins_tuple, 622 } 623 624 # vim: tabstop=4 expandtab shiftwidth=4