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