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(self): 380 frame = self.local_sp_stack[-1] 381 382 # Get the list address. 383 384 obj_value = self.frame_stack[frame] 385 386 if not self.machine._CheckInstance(obj_value.ref, self.list_class): 387 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 388 return self.machine.RaiseException() 389 390 # Get the fragment address. 391 392 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 393 394 # Get the fragment header. 395 396 header = self.machine.load(fragment.ref) 397 398 # Make a new object. 399 400 addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance) 401 402 # Copy the fragment contents into the tuple. 403 # NOTE: This might be done by repurposing the fragment in some situations. 404 405 for i in range(self.fragment_data_offset, header.occupied_size): 406 self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i)) 407 408 # Return the new object. 409 # Introduce object as context for the new object. 410 411 self.machine.result = DataValue(addr, addr) 412 413 def builtins_tuple_len(self): 414 frame = self.local_sp_stack[-1] 415 416 # Get the tuple address. 417 418 obj_value = self.frame_stack[frame] 419 420 # Get the header. 421 422 header = self.machine.load(obj_value.ref) 423 nelements = header.size - self.instance_data_offset 424 425 # Make a new object. 426 427 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 428 429 # Store the result. 430 # NOTE: The data is considered ready to use. 431 432 self.machine.save(addr + self.instance_data_offset, nelements) 433 434 # Return the new object. 435 # Introduce object as context for the new object. 436 437 self.machine.result = DataValue(addr, addr) 438 439 def builtins_tuple_get_single_item(self): 440 frame = self.local_sp_stack[-1] 441 442 # Get the operand address. 443 444 item_value = self.frame_stack[frame + 1] 445 446 # Get the tuple address. 447 448 obj_value = self.frame_stack[frame] 449 450 # Get the header. 451 452 header = self.machine.load(obj_value.ref) 453 nelements = header.size - self.instance_data_offset 454 455 # NOTE: Assume single location for data and header. 456 457 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 458 459 if not self._check_index(item_pos, nelements): 460 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 461 return self.machine.RaiseException() 462 463 # Get the item. 464 465 self.machine.result = self.machine.load(obj_value.ref + self.instance_data_offset + item_pos) 466 467 def builtins_object_init(self): 468 pass 469 470 def builtins_getattr(self): 471 frame = self.local_sp_stack[-1] 472 473 # Get the object, attribute name. 474 475 obj_value = self.frame_stack[frame] 476 name_value = self.frame_stack[frame + 1] 477 478 if not self.machine._CheckInstance(name_value.ref, self.accessor_class): 479 self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance) 480 return self.machine.RaiseException() 481 482 # Get the object table index from the name. It is a bare integer, not a reference. 483 484 index = self.machine.load(name_value.ref + self.instance_data_offset + 1) 485 486 # NOTE: This is very much like LoadAttrIndex. 487 488 data = self.machine.load(obj_value.ref) 489 element = self.machine.objlist[data.classcode + index] 490 491 if element is not None: 492 attr_index, static_attr, offset = element 493 if attr_index == index: 494 if static_attr: 495 self.machine.result = self.machine.load(offset) # offset is address of class/module attribute 496 else: 497 self.machine.result = self.machine.load(obj_value.ref + offset) 498 return 499 500 self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance) 501 return self.machine.RaiseException() 502 503 def builtins_isinstance(self): 504 frame = self.local_sp_stack[-1] 505 506 # Get the operand addresses. 507 508 obj_value = self.frame_stack[frame] 509 cls_value = self.frame_stack[frame + 1] 510 511 if self.machine._CheckInstance(obj_value.ref, cls_value.ref): 512 self.machine.result = DataValue(self.constants[True], self.constants[True]) 513 else: 514 self.machine.result = DataValue(self.constants[False], self.constants[False]) 515 516 def builtins_print(self): 517 # NOTE: Do nothing for now. 518 pass 519 520 def builtins_printnl(self): 521 # NOTE: Do nothing for now. 522 pass 523 524 native_functions = { 525 526 # Native method implementations: 527 528 "__builtins__.int.__add__" : builtins_int_add, 529 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 530 "__builtins__.int.__sub__" : builtins_int_sub, 531 "__builtins__.int.__pow__" : builtins_int_pow, 532 "__builtins__.int.__iadd__" : builtins_int_add, 533 "__builtins__.int.__bool__" : builtins_int_bool, 534 "__builtins__.int.__neg__" : builtins_int_neg, 535 "__builtins__.int.__lt__" : builtins_int_lt, 536 "__builtins__.int.__le__" : builtins_int_le, 537 "__builtins__.int.__gt__" : builtins_int_gt, 538 "__builtins__.int.__ge__" : builtins_int_ge, 539 "__builtins__.int.__eq__" : builtins_int_eq, 540 "__builtins__.int.__ne__" : builtins_int_ne, 541 "__builtins__.int.__and__" : builtins_int_and, 542 "__builtins__.int.__rand__" : builtins_int_and, 543 "__builtins__.int.__or__" : builtins_int_or, 544 "__builtins__.int.__ror__" : builtins_int_or, 545 "__builtins__.bool.__bool__" : builtins_bool_bool, 546 "__builtins__.list.__get_single_item__" : builtins_list_get_single_item, 547 "__builtins__.list.__len__" : builtins_list_len, 548 "__builtins__.list.append" : builtins_list_append, 549 "__builtins__.tuple.__len__" : builtins_tuple_len, 550 "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item, 551 "__builtins__.basestring.__lt__" : builtins_str_lt, 552 "__builtins__.basestring.__le__" : builtins_str_le, 553 "__builtins__.basestring.__gt__" : builtins_str_gt, 554 "__builtins__.basestring.__ge__" : builtins_str_ge, 555 "__builtins__.basestring.__eq__" : builtins_str_eq, 556 "__builtins__.basestring.__ne__" : builtins_str_ne, 557 558 # Native initialisers: 559 560 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 561 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 562 563 # Native functions: 564 565 "__builtins__._getattr" : builtins_getattr, 566 567 # Native instantiator helpers: 568 569 "__builtins__.list.__new__" : builtins_list_new, 570 571 # Native helper functions: 572 573 "__builtins__._isinstance" : builtins_isinstance, 574 "__builtins__._print" : builtins_print, 575 "__builtins__._printnl" : builtins_printnl, 576 "__builtins__._tuple" : builtins_tuple, 577 } 578 579 # vim: tabstop=4 expandtab shiftwidth=4