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