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