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