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