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 # NOTE: These attributes need changing if the instance layout changes. 30 31 instance_data_offset = 2 32 instance_size = 3 33 34 def __init__(self, machine, constants): 35 36 """ 37 Initialise the library with the 'machine' and the 'constants' addresses 38 dictionary. 39 """ 40 41 self.machine = machine 42 self.constants = constants 43 44 # Native class constants. 45 46 cls = self.machine._get_class("__builtins__", "int") 47 self.int_class = cls.location 48 self.int_instance = cls.instance_template_location 49 cls = self.machine._get_class("__builtins__", "list") 50 if cls is not None: 51 self.list_class = cls.location 52 self.list_instance = cls.instance_template_location 53 cls = self.machine._get_class("__builtins__", "IndexError") 54 if cls is not None: 55 self.index_error = cls.location 56 self.index_error_instance = cls.instance_template_location 57 cls = self.machine._get_class("__builtins__", "basestring") 58 if cls is not None: 59 self.str_class = cls.location 60 self.str_instance = cls.instance_template_location 61 62 self.tuple_class = self.machine.tuple_class 63 self.type_error_instance = self.machine.type_error_instance 64 65 self.frame_stack = self.machine.frame_stack 66 self.local_sp_stack = self.machine.local_sp_stack 67 68 def _check_index(self, pos, nelements): 69 return pos >= 0 and pos < nelements 70 71 def builtins_int_arithmetic_op(self, op): 72 frame = self.local_sp_stack[-1] 73 74 # Get operands addresses. 75 76 left_value = self.frame_stack[frame] 77 right_value = self.frame_stack[frame + 1] 78 79 # Test operand suitability. 80 # NOTE: Support other types. 81 82 if not (self.machine._CheckInstance(left_value.ref, self.int_class) and 83 self.machine._CheckInstance(right_value.ref, self.int_class)): 84 85 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 86 return self.machine.RaiseException() 87 88 left_data = left_value.ref + self.instance_data_offset 89 right_data = right_value.ref + self.instance_data_offset 90 91 # Make a new object. 92 93 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 94 95 # Store the result. 96 # NOTE: The data is considered ready to use. 97 98 self.machine.save(addr + self.instance_data_offset, op(self.machine.load(left_data), self.machine.load(right_data))) 99 100 # Return the new object. 101 # Introduce object as context for the new object. 102 103 self.machine.result = DataValue(addr, addr) 104 105 def builtins_logical_op(self, operand_class, op): 106 frame = self.local_sp_stack[-1] 107 108 # Get operands addresses. 109 110 left_value = self.frame_stack[frame] 111 right_value = self.frame_stack[frame + 1] 112 113 # Test operand suitability. 114 # NOTE: Handle comparisons of incompatible types more appropriately. 115 # NOTE: Return NotImplemented. 116 117 if not (self.machine._CheckInstance(left_value.ref, operand_class) and 118 self.machine._CheckInstance(right_value.ref, operand_class)): 119 120 notimpl = self.constants[NotImplemented] 121 self.machine.result = DataValue(notimpl, notimpl) 122 return 123 124 left_data = left_value.ref + self.instance_data_offset 125 right_data = right_value.ref + self.instance_data_offset 126 127 # Test the data. 128 # NOTE: The data is considered ready to use. 129 130 if op(self.machine.load(left_data), self.machine.load(right_data)): 131 self.machine.result = DataValue(self.constants[True], self.constants[True]) 132 else: 133 self.machine.result = DataValue(self.constants[False], self.constants[False]) 134 135 # Operators. 136 # Although this takes a short-cut by using the operator module, testing is 137 # still performed on the operands to ensure that they qualify for these 138 # native operations. 139 140 def builtins_int_add(self): 141 return self.builtins_int_arithmetic_op(operator.add) 142 143 def builtins_int_sub(self): 144 return self.builtins_int_arithmetic_op(operator.sub) 145 146 def builtins_int_pow(self): 147 return self.builtins_int_arithmetic_op(operator.pow) 148 149 def builtins_int_lt(self): 150 return self.builtins_logical_op(self.int_class, operator.lt) 151 152 def builtins_int_le(self): 153 return self.builtins_logical_op(self.int_class, operator.le) 154 155 def builtins_int_gt(self): 156 return self.builtins_logical_op(self.int_class, operator.gt) 157 158 def builtins_int_ge(self): 159 return self.builtins_logical_op(self.int_class, operator.ge) 160 161 def builtins_int_eq(self): 162 return self.builtins_logical_op(self.int_class, operator.eq) 163 164 def builtins_int_ne(self): 165 return self.builtins_logical_op(self.int_class, operator.ne) 166 167 def builtins_str_lt(self): 168 return self.builtins_logical_op(self.str_class, operator.lt) 169 170 def builtins_str_le(self): 171 return self.builtins_logical_op(self.str_class, operator.le) 172 173 def builtins_str_gt(self): 174 return self.builtins_logical_op(self.str_class, operator.gt) 175 176 def builtins_str_ge(self): 177 return self.builtins_logical_op(self.str_class, operator.ge) 178 179 def builtins_str_eq(self): 180 return self.builtins_logical_op(self.str_class, operator.eq) 181 182 def builtins_str_ne(self): 183 return self.builtins_logical_op(self.str_class, operator.ne) 184 185 def builtins_int_and(self): 186 return self.builtins_int_arithmetic_op(operator.and_) 187 188 def builtins_int_or(self): 189 return self.builtins_int_arithmetic_op(operator.or_) 190 191 # Specific operator methods. 192 193 def builtins_int_bool(self): 194 frame = self.local_sp_stack[-1] 195 196 # Get operands addresses. 197 198 left_value = self.frame_stack[frame] 199 200 # Test operand suitability. 201 202 if not self.machine._CheckInstance(left_value.ref, self.int_class): 203 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 204 return self.machine.RaiseException() 205 206 left_data = left_value.ref + self.instance_data_offset 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.constants[True], self.constants[True]) 213 else: 214 self.machine.result = DataValue(self.constants[False], self.constants[False]) 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(self.instance_size, self.type_error_instance) 227 return self.machine.RaiseException() 228 229 left_data = left_value.ref + self.instance_data_offset 230 231 # Make a new object. 232 233 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 234 235 # Store the result. 236 # NOTE: The data is considered ready to use. 237 238 self.machine.save(addr + self.instance_data_offset, -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 + self.instance_data_offset 271 self.machine.save(addr, DataValue(None, new_fragment)) 272 273 def builtins_list_get_single_item(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 286 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 287 288 # Get the fragment header. 289 290 header = self.machine.load(fragment.ref) 291 nelements = header.occupied_size - 1 292 293 # Get the item position. 294 295 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 296 297 if not self._check_index(item_pos, nelements): 298 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 299 return self.machine.RaiseException() 300 301 # Get the item itself. 302 303 self.machine.result = self.machine.load(fragment.ref + self.instance_data_offset + item_pos) 304 305 def builtins_list_len(self): 306 frame = self.local_sp_stack[-1] 307 308 # Get the list address. 309 310 obj_value = self.frame_stack[frame] 311 312 # Get the fragment address. 313 314 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 315 316 # Get the fragment header. 317 318 header = self.machine.load(fragment.ref) 319 nelements = header.occupied_size - 1 320 321 # Make a new object. 322 323 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 324 325 # Store the result. 326 # NOTE: The data is considered ready to use. 327 328 self.machine.save(addr + self.instance_data_offset, nelements) 329 330 # Return the new object. 331 # Introduce object as context for the new object. 332 333 self.machine.result = DataValue(addr, addr) 334 335 def builtins_list_append(self): 336 frame = self.local_sp_stack[-1] 337 338 # Get operand address. 339 340 arg_value = self.frame_stack[frame + 1] 341 342 # Get the list address. 343 344 obj_value = self.frame_stack[frame] 345 346 # Get the fragment address. 347 348 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 349 350 # Get the fragment header. 351 352 header = self.machine.load(fragment.ref) 353 354 # Attempt to add the reference. 355 356 if header.occupied_size < header.allocated_size: 357 self.machine.save(fragment.ref + header.occupied_size, arg_value) 358 header.occupied_size += 1 359 else: 360 361 # Make a new fragment, maintaining more space than currently 362 # occupied in order to avoid reallocation. 363 364 new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2) 365 366 # Copy existing elements. 367 368 for i in range(1, header.occupied_size): 369 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i)) 370 371 self.machine.save(new_fragment + header.occupied_size, arg_value) 372 373 # Set the new fragment in the object. 374 # NOTE: The old fragment could be deallocated. 375 376 self.machine.save(obj_value.ref + self.instance_data_offset, DataValue(None, new_fragment)) 377 378 def builtins_tuple_len(self): 379 frame = self.local_sp_stack[-1] 380 381 # Get the tuple address. 382 383 obj_value = self.frame_stack[frame] 384 385 # Get the header. 386 387 header = self.machine.load(obj_value.ref) 388 nelements = header.size - self.instance_data_offset 389 390 # Make a new object. 391 392 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 393 394 # Store the result. 395 # NOTE: The data is considered ready to use. 396 397 self.machine.save(addr + self.instance_data_offset, nelements) 398 399 # Return the new object. 400 # Introduce object as context for the new object. 401 402 self.machine.result = DataValue(addr, addr) 403 404 def builtins_tuple_get_single_item(self): 405 frame = self.local_sp_stack[-1] 406 407 # Get the operand address. 408 409 item_value = self.frame_stack[frame + 1] 410 411 # Get the tuple address. 412 413 obj_value = self.frame_stack[frame] 414 415 # Get the header. 416 417 header = self.machine.load(obj_value.ref) 418 nelements = header.size - self.instance_data_offset 419 420 # NOTE: Assume single location for data and header. 421 422 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 423 424 if not self._check_index(item_pos, nelements): 425 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 426 return self.machine.RaiseException() 427 428 # Get the item. 429 430 self.machine.result = self.machine.load(obj_value.ref + self.instance_data_offset + item_pos) 431 432 def builtins_object_init(self): 433 pass 434 435 def builtins_isinstance(self): 436 frame = self.local_sp_stack[-1] 437 438 # Get the operand addresses. 439 440 obj_value = self.frame_stack[frame] 441 cls_value = self.frame_stack[frame + 1] 442 443 if self.machine._CheckInstance(obj_value.ref, cls_value.ref): 444 self.machine.result = DataValue(self.constants[True], self.constants[True]) 445 else: 446 self.machine.result = DataValue(self.constants[False], self.constants[False]) 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.__get_single_item__" : builtins_list_get_single_item, 471 "__builtins__.list.__len__" : builtins_list_len, 472 "__builtins__.list.append" : builtins_list_append, 473 "__builtins__.tuple.__len__" : builtins_tuple_len, 474 "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item, 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 # Native helper functions: 492 493 "__builtins__._isinstance" : builtins_isinstance, 494 } 495 496 # vim: tabstop=4 expandtab shiftwidth=4