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