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