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 cls = self.machine._get_class("__builtins__", "int") 48 self.int_class = cls.location 49 self.int_instance = cls.instance_template_location 50 cls = self.machine._get_class("__builtins__", "list") 51 if cls is not None: 52 self.list_class = cls.location 53 self.list_instance = cls.instance_template_location 54 cls = self.machine._get_class("__builtins__", "IndexError") 55 if cls is not None: 56 self.index_error = cls.location 57 self.index_error_instance = cls.instance_template_location 58 cls = self.machine._get_class("__builtins__", "basestring") 59 if cls is not None: 60 self.str_class = cls.location 61 self.str_instance = cls.instance_template_location 62 63 self.tuple_class = self.machine.tuple_class 64 self.tuple_instance = self.machine.tuple_instance 65 self.type_error_instance = self.machine.type_error_instance 66 67 self.frame_stack = self.machine.frame_stack 68 self.local_sp_stack = self.machine.local_sp_stack 69 70 def _check_index(self, pos, nelements): 71 return pos >= 0 and pos < nelements 72 73 def builtins_int_arithmetic_op(self, op): 74 frame = self.local_sp_stack[-1] 75 76 # Get operands addresses. 77 78 left_value = self.frame_stack[frame] 79 right_value = self.frame_stack[frame + 1] 80 81 # Test operand suitability. 82 # NOTE: Support other types. 83 84 if not (self.machine._CheckInstance(left_value.ref, self.int_class) and 85 self.machine._CheckInstance(right_value.ref, self.int_class)): 86 87 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 88 return self.machine.RaiseException() 89 90 left_data = left_value.ref + self.instance_data_offset 91 right_data = right_value.ref + self.instance_data_offset 92 93 # Make a new object. 94 95 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 96 97 # Store the result. 98 # NOTE: The data is considered ready to use. 99 100 self.machine.save(addr + self.instance_data_offset, op(self.machine.load(left_data), self.machine.load(right_data))) 101 102 # Return the new object. 103 # Introduce object as context for the new object. 104 105 self.machine.result = DataValue(addr, 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 = DataValue(notimpl, notimpl) 124 return 125 126 left_data = left_value.ref + self.instance_data_offset 127 right_data = right_value.ref + self.instance_data_offset 128 129 # Test the data. 130 # NOTE: The data is considered ready to use. 131 132 if op(self.machine.load(left_data), self.machine.load(right_data)): 133 self.machine.result = DataValue(self.constants[True], self.constants[True]) 134 else: 135 self.machine.result = DataValue(self.constants[False], self.constants[False]) 136 137 # Operators. 138 # Although this takes a short-cut by using the operator module, testing is 139 # still performed on the operands to ensure that they qualify for these 140 # native operations. 141 142 def builtins_int_add(self): 143 return self.builtins_int_arithmetic_op(operator.add) 144 145 def builtins_int_sub(self): 146 return self.builtins_int_arithmetic_op(operator.sub) 147 148 def builtins_int_pow(self): 149 return self.builtins_int_arithmetic_op(operator.pow) 150 151 def builtins_int_lt(self): 152 return self.builtins_logical_op(self.int_class, operator.lt) 153 154 def builtins_int_le(self): 155 return self.builtins_logical_op(self.int_class, operator.le) 156 157 def builtins_int_gt(self): 158 return self.builtins_logical_op(self.int_class, operator.gt) 159 160 def builtins_int_ge(self): 161 return self.builtins_logical_op(self.int_class, operator.ge) 162 163 def builtins_int_eq(self): 164 return self.builtins_logical_op(self.int_class, operator.eq) 165 166 def builtins_int_ne(self): 167 return self.builtins_logical_op(self.int_class, operator.ne) 168 169 def builtins_str_lt(self): 170 return self.builtins_logical_op(self.str_class, operator.lt) 171 172 def builtins_str_le(self): 173 return self.builtins_logical_op(self.str_class, operator.le) 174 175 def builtins_str_gt(self): 176 return self.builtins_logical_op(self.str_class, operator.gt) 177 178 def builtins_str_ge(self): 179 return self.builtins_logical_op(self.str_class, operator.ge) 180 181 def builtins_str_eq(self): 182 return self.builtins_logical_op(self.str_class, operator.eq) 183 184 def builtins_str_ne(self): 185 return self.builtins_logical_op(self.str_class, operator.ne) 186 187 def builtins_int_and(self): 188 return self.builtins_int_arithmetic_op(operator.and_) 189 190 def builtins_int_or(self): 191 return self.builtins_int_arithmetic_op(operator.or_) 192 193 # Specific operator methods. 194 195 def builtins_int_bool(self): 196 frame = self.local_sp_stack[-1] 197 198 # Get operands addresses. 199 200 left_value = self.frame_stack[frame] 201 202 # Test operand suitability. 203 204 if not self.machine._CheckInstance(left_value.ref, self.int_class): 205 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 206 return self.machine.RaiseException() 207 208 left_data = left_value.ref + self.instance_data_offset 209 210 # Test the data. 211 # NOTE: The data is considered ready to use. 212 213 if self.machine.load(left_data) != 0: 214 self.machine.result = DataValue(self.constants[True], self.constants[True]) 215 else: 216 self.machine.result = DataValue(self.constants[False], self.constants[False]) 217 218 def builtins_int_neg(self): 219 frame = self.local_sp_stack[-1] 220 221 # Get operands addresses. 222 223 left_value = self.frame_stack[frame] 224 225 # Test operand suitability. 226 227 if not self.machine._CheckInstance(left_value.ref, self.int_class): 228 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 229 return self.machine.RaiseException() 230 231 left_data = left_value.ref + self.instance_data_offset 232 233 # Make a new object. 234 235 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 236 237 # Store the result. 238 # NOTE: The data is considered ready to use. 239 240 self.machine.save(addr + self.instance_data_offset, -self.machine.load(left_data)) 241 242 # Return the new object. 243 # Introduce object as context for the new object. 244 245 self.machine.result = DataValue(addr, addr) 246 247 # Various built-in methods. 248 249 def builtins_bool_bool(self): 250 frame = self.local_sp_stack[-1] 251 252 # Get operands addresses. 253 254 left_value = self.frame_stack[frame] 255 self.machine.result = DataValue(left_value.ref, left_value.ref) 256 257 def builtins_list_new(self): 258 frame = self.local_sp_stack[-1] 259 260 # The first parameter should be the instance. 261 262 list_value = self.frame_stack[frame] 263 264 # Make a new sequence. 265 # NOTE: Using an arbitrary size. 266 267 new_fragment = self.machine._MakeFragment(self.fragment_data_offset, 5) # include the header 268 269 # Complete the list instance by saving the fragment reference. 270 # NOTE: This requires an attribute in the list structure. 271 272 addr = list_value.ref + self.instance_data_offset 273 self.machine.save(addr, DataValue(None, new_fragment)) 274 275 def builtins_list_get_single_item(self): 276 frame = self.local_sp_stack[-1] 277 278 # Get the operand address. 279 280 item_value = self.frame_stack[frame + 1] 281 282 # Get the list address. 283 284 obj_value = self.frame_stack[frame] 285 286 # Get the fragment address. 287 288 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 289 290 # Get the fragment header. 291 292 header = self.machine.load(fragment.ref) 293 nelements = header.occupied_size - self.fragment_data_offset 294 295 # Get the item position. 296 297 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 298 299 if not self._check_index(item_pos, nelements): 300 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 301 return self.machine.RaiseException() 302 303 # Get the item itself. 304 305 self.machine.result = self.machine.load(fragment.ref + self.fragment_data_offset + item_pos) 306 307 def builtins_list_len(self): 308 frame = self.local_sp_stack[-1] 309 310 # Get the list address. 311 312 obj_value = self.frame_stack[frame] 313 314 # Get the fragment address. 315 316 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 317 318 # Get the fragment header. 319 320 header = self.machine.load(fragment.ref) 321 nelements = header.occupied_size - self.fragment_data_offset 322 323 # Make a new object. 324 325 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 326 327 # Store the result. 328 # NOTE: The data is considered ready to use. 329 330 self.machine.save(addr + self.instance_data_offset, nelements) 331 332 # Return the new object. 333 # Introduce object as context for the new object. 334 335 self.machine.result = DataValue(addr, addr) 336 337 def builtins_list_append(self): 338 frame = self.local_sp_stack[-1] 339 340 # Get operand address. 341 342 arg_value = self.frame_stack[frame + 1] 343 344 # Get the list address. 345 346 obj_value = self.frame_stack[frame] 347 348 # Get the fragment address. 349 350 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 351 352 # Get the fragment header. 353 354 header = self.machine.load(fragment.ref) 355 356 # Attempt to add the reference. 357 358 if header.occupied_size < header.allocated_size: 359 self.machine.save(fragment.ref + header.occupied_size, arg_value) 360 header.occupied_size += 1 361 else: 362 363 # Make a new fragment, maintaining more space than currently 364 # occupied in order to avoid reallocation. 365 366 new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2) 367 368 # Copy existing elements. 369 370 for i in range(self.fragment_data_offset, header.occupied_size): 371 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i)) 372 373 self.machine.save(new_fragment + header.occupied_size, arg_value) 374 375 # Set the new fragment in the object. 376 # NOTE: The old fragment could be deallocated. 377 378 self.machine.save(obj_value.ref + self.instance_data_offset, DataValue(None, new_fragment)) 379 380 def builtins_tuple(self): 381 frame = self.local_sp_stack[-1] 382 383 # Get the list address. 384 385 obj_value = self.frame_stack[frame] 386 387 if not self.machine._CheckInstance(obj_value.ref, self.list_class): 388 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 389 return self.machine.RaiseException() 390 391 # Get the fragment address. 392 393 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 394 395 # Get the fragment header. 396 397 header = self.machine.load(fragment.ref) 398 399 # Make a new object. 400 401 addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance) 402 403 # Copy the fragment contents into the tuple. 404 # NOTE: This might be done by repurposing the fragment in some situations. 405 406 for i in range(self.fragment_data_offset, header.occupied_size): 407 self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i)) 408 409 # Return the new object. 410 # Introduce object as context for the new object. 411 412 self.machine.result = DataValue(addr, addr) 413 414 def builtins_tuple_len(self): 415 frame = self.local_sp_stack[-1] 416 417 # Get the tuple address. 418 419 obj_value = self.frame_stack[frame] 420 421 # Get the header. 422 423 header = self.machine.load(obj_value.ref) 424 nelements = header.size - self.instance_data_offset 425 426 # Make a new object. 427 428 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 429 430 # Store the result. 431 # NOTE: The data is considered ready to use. 432 433 self.machine.save(addr + self.instance_data_offset, nelements) 434 435 # Return the new object. 436 # Introduce object as context for the new object. 437 438 self.machine.result = DataValue(addr, addr) 439 440 def builtins_tuple_get_single_item(self): 441 frame = self.local_sp_stack[-1] 442 443 # Get the operand address. 444 445 item_value = self.frame_stack[frame + 1] 446 447 # Get the tuple address. 448 449 obj_value = self.frame_stack[frame] 450 451 # Get the header. 452 453 header = self.machine.load(obj_value.ref) 454 nelements = header.size - self.instance_data_offset 455 456 # NOTE: Assume single location for data and header. 457 458 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 459 460 if not self._check_index(item_pos, nelements): 461 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 462 return self.machine.RaiseException() 463 464 # Get the item. 465 466 self.machine.result = self.machine.load(obj_value.ref + self.instance_data_offset + item_pos) 467 468 def builtins_object_init(self): 469 pass 470 471 def builtins_isinstance(self): 472 frame = self.local_sp_stack[-1] 473 474 # Get the operand addresses. 475 476 obj_value = self.frame_stack[frame] 477 cls_value = self.frame_stack[frame + 1] 478 479 if self.machine._CheckInstance(obj_value.ref, cls_value.ref): 480 self.machine.result = DataValue(self.constants[True], self.constants[True]) 481 else: 482 self.machine.result = DataValue(self.constants[False], self.constants[False]) 483 484 def builtins_print(self): 485 # NOTE: Do nothing for now. 486 pass 487 488 def builtins_printnl(self): 489 # NOTE: Do nothing for now. 490 pass 491 492 native_functions = { 493 494 # Native method implementations: 495 496 "__builtins__.int.__add__" : builtins_int_add, 497 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 498 "__builtins__.int.__sub__" : builtins_int_sub, 499 "__builtins__.int.__pow__" : builtins_int_pow, 500 "__builtins__.int.__iadd__" : builtins_int_add, 501 "__builtins__.int.__bool__" : builtins_int_bool, 502 "__builtins__.int.__neg__" : builtins_int_neg, 503 "__builtins__.int.__lt__" : builtins_int_lt, 504 "__builtins__.int.__le__" : builtins_int_le, 505 "__builtins__.int.__gt__" : builtins_int_gt, 506 "__builtins__.int.__ge__" : builtins_int_ge, 507 "__builtins__.int.__eq__" : builtins_int_eq, 508 "__builtins__.int.__ne__" : builtins_int_ne, 509 "__builtins__.int.__and__" : builtins_int_and, 510 "__builtins__.int.__rand__" : builtins_int_and, 511 "__builtins__.int.__or__" : builtins_int_or, 512 "__builtins__.int.__ror__" : builtins_int_or, 513 "__builtins__.bool.__bool__" : builtins_bool_bool, 514 "__builtins__.list.__get_single_item__" : builtins_list_get_single_item, 515 "__builtins__.list.__len__" : builtins_list_len, 516 "__builtins__.list.append" : builtins_list_append, 517 "__builtins__.tuple.__len__" : builtins_tuple_len, 518 "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item, 519 "__builtins__.basestring.__lt__" : builtins_str_lt, 520 "__builtins__.basestring.__le__" : builtins_str_le, 521 "__builtins__.basestring.__gt__" : builtins_str_gt, 522 "__builtins__.basestring.__ge__" : builtins_str_ge, 523 "__builtins__.basestring.__eq__" : builtins_str_eq, 524 "__builtins__.basestring.__ne__" : builtins_str_ne, 525 526 # Native initialisers: 527 528 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 529 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 530 531 # Native instantiator helpers: 532 533 "__builtins__.list.__new__" : builtins_list_new, 534 535 # Native helper functions: 536 537 "__builtins__._isinstance" : builtins_isinstance, 538 "__builtins__._print" : builtins_print, 539 "__builtins__._printnl" : builtins_printnl, 540 "__builtins__._tuple" : builtins_tuple, 541 } 542 543 # vim: tabstop=4 expandtab shiftwidth=4