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 # NOTE: Assume single location for data and header. 295 296 item_pos = self.machine.load(item_value.ref + 1) 297 298 if item_pos >= 0 and item_pos < nelements: 299 pass 300 elif item_pos < 0 and item_pos >= -nelements: 301 item_pos = nelements + item_pos 302 else: 303 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 304 return self.machine.RaiseException() 305 306 # NOTE: Assume single location for header. 307 308 self.machine.result = self.machine.load(fragment.ref + 1 + item_pos) 309 310 def builtins_list_len(self): 311 frame = self.local_sp_stack[-1] 312 313 # Get the list address. 314 315 obj_value = self.frame_stack[frame] 316 317 # Get the fragment address. 318 # NOTE: Assume single location for header. 319 320 fragment = self.machine.load(obj_value.ref + 1) 321 322 # Get the fragment header. 323 324 header = self.machine.load(fragment.ref) 325 nelements = header.occupied_size - 1 326 327 # Make a new object. 328 329 addr = self.machine._MakeObject(2, self.int_instance) 330 331 # Store the result. 332 # NOTE: The data is considered ready to use. 333 334 self.machine.save(addr + 1, nelements) 335 336 # Return the new object. 337 # Introduce object as context for the new object. 338 339 self.machine.result = DataValue(addr, addr) 340 341 def builtins_list_append(self): 342 frame = self.local_sp_stack[-1] 343 344 # Get operand address. 345 346 arg_value = self.frame_stack[frame + 1] 347 348 # Get the list address. 349 350 obj_value = self.frame_stack[frame] 351 352 # Get the fragment address. 353 # NOTE: Assume single location for header. 354 355 fragment = self.machine.load(obj_value.ref + 1) 356 357 # Get the fragment header. 358 359 header = self.machine.load(fragment.ref) 360 361 # Attempt to add the reference. 362 363 if header.occupied_size < header.allocated_size: 364 self.machine.save(fragment.ref + header.occupied_size, arg_value) 365 header.occupied_size += 1 366 else: 367 368 # Make a new fragment, maintaining more space than currently 369 # occupied in order to avoid reallocation. 370 371 new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2) 372 373 # Copy existing elements. 374 375 for i in range(1, header.occupied_size): 376 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i)) 377 378 self.machine.save(new_fragment + header.occupied_size, arg_value) 379 380 # Set the new fragment in the object. 381 # NOTE: The old fragment could be deallocated. 382 383 self.machine.save(obj_value.ref + 1, DataValue(None, new_fragment)) 384 385 def builtins_tuple_len(self): 386 frame = self.local_sp_stack[-1] 387 388 # Get the tuple address. 389 390 obj_value = self.frame_stack[frame] 391 392 # Get the header. 393 # NOTE: Assume single location for header. 394 395 header = self.machine.load(obj_value.ref) 396 nelements = header.size - 1 397 398 # Make a new object. 399 400 addr = self.machine._MakeObject(2, self.int_instance) 401 402 # Store the result. 403 # NOTE: The data is considered ready to use. 404 405 self.machine.save(addr + 1, nelements) 406 407 # Return the new object. 408 # Introduce object as context for the new object. 409 410 self.machine.result = DataValue(addr, addr) 411 412 def builtins_tuple_getitem(self): 413 frame = self.local_sp_stack[-1] 414 415 # Get the operand address. 416 417 item_value = self.frame_stack[frame + 1] 418 419 # Get the tuple address. 420 421 obj_value = self.frame_stack[frame] 422 423 # Get the header. 424 # NOTE: Assume single location for header. 425 426 header = self.machine.load(obj_value.ref) 427 nelements = header.size - 1 428 429 # NOTE: Assume single location for data and header. 430 431 item_pos = self.machine.load(item_value.ref + 1) 432 433 if item_pos >= 0 and item_pos < nelements: 434 pass 435 elif item_pos < 0 and item_pos >= -nelements: 436 item_pos = nelements + item_pos 437 else: 438 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 439 return self.machine.RaiseException() 440 441 # NOTE: Assume single location for header. 442 443 self.machine.result = self.machine.load(obj_value.ref + 1 + item_pos) 444 445 def builtins_object_init(self): 446 pass 447 448 native_functions = { 449 450 # Native method implementations: 451 452 "__builtins__.int.__add__" : builtins_int_add, 453 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 454 "__builtins__.int.__sub__" : builtins_int_sub, 455 "__builtins__.int.__pow__" : builtins_int_pow, 456 "__builtins__.int.__iadd__" : builtins_int_add, 457 "__builtins__.int.__bool__" : builtins_int_bool, 458 "__builtins__.int.__neg__" : builtins_int_neg, 459 "__builtins__.int.__lt__" : builtins_int_lt, 460 "__builtins__.int.__le__" : builtins_int_le, 461 "__builtins__.int.__gt__" : builtins_int_gt, 462 "__builtins__.int.__ge__" : builtins_int_ge, 463 "__builtins__.int.__eq__" : builtins_int_eq, 464 "__builtins__.int.__ne__" : builtins_int_ne, 465 "__builtins__.int.__and__" : builtins_int_and, 466 "__builtins__.int.__rand__" : builtins_int_and, 467 "__builtins__.int.__or__" : builtins_int_or, 468 "__builtins__.int.__ror__" : builtins_int_or, 469 "__builtins__.bool.__bool__" : builtins_bool_bool, 470 "__builtins__.list.__getitem__" : builtins_list_getitem, 471 "__builtins__.list.__len__" : builtins_list_len, 472 "__builtins__.list.append" : builtins_list_append, 473 "__builtins__.tuple.__len__" : builtins_tuple_len, 474 "__builtins__.tuple.__getitem__" : builtins_tuple_getitem, 475 "__builtins__.basestring.__lt__" : builtins_str_lt, 476 "__builtins__.basestring.__le__" : builtins_str_le, 477 "__builtins__.basestring.__gt__" : builtins_str_gt, 478 "__builtins__.basestring.__ge__" : builtins_str_ge, 479 "__builtins__.basestring.__eq__" : builtins_str_eq, 480 "__builtins__.basestring.__ne__" : builtins_str_ne, 481 482 # Native initialisers: 483 484 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 485 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 486 487 # Native instantiator helpers: 488 489 "__builtins__.list.__new__" : builtins_list_new, 490 } 491 492 # vim: tabstop=4 expandtab shiftwidth=4