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