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, 2011 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_template_size = instance_data_offset = 1 32 instance_size = instance_template_size + 1 33 fragment_data_offset = 1 34 35 def __init__(self, machine, constants): 36 37 """ 38 Initialise the library with the 'machine' and the 'constants' addresses 39 dictionary. 40 """ 41 42 self.machine = machine 43 self.constants = constants 44 45 # Native class constants. 46 47 self.int_class, self.int_instance = self._get_builtin_class_and_template("int") 48 self.list_class, self.list_instance = self._get_builtin_class_and_template("list") 49 self.index_error, self.index_error_instance = self._get_builtin_class_and_template("IndexError") 50 self.str_class, self.str_instance = self._get_builtin_class_and_template("basestring") 51 self.accessor_class, self.accessor_instance = self._get_builtin_class_and_template("_accessor") 52 53 self.tuple_class = self.machine.tuple_class 54 self.tuple_instance = self.machine.tuple_instance 55 56 self.attr_error_instance = self.machine.attr_error_instance 57 self.type_error_instance = self.machine.type_error_instance 58 59 self.frame_stack = self.machine.frame_stack 60 self.local_sp_stack = self.machine.local_sp_stack 61 62 def _get_builtin_class_and_template(self, name): 63 cls = self.machine._get_class("__builtins__", name) 64 if cls is not None: 65 return cls.location, cls.instance_template_location 66 else: 67 return None, None 68 69 def _check_index(self, pos, nelements): 70 return pos >= 0 and pos < nelements 71 72 # Native functionality. 73 74 def builtins_no_op(self): 75 pass 76 77 def native_int_arithmetic_op(self, op): 78 self.machine.LoadName(0) # left value 79 left_data = self.machine.value + self.instance_data_offset 80 self.machine.LoadName(1) # right value 81 right_data = self.machine.value + self.instance_data_offset 82 83 # Make a new object. 84 85 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 86 87 # Store the result. 88 # NOTE: The data is considered ready to use. 89 90 self.machine.save(addr + self.instance_data_offset, op(self.machine.load(left_data), self.machine.load(right_data))) 91 92 # Return the new object. 93 # Introduce object as context for the new object. 94 95 self.machine.result_context = addr 96 self.machine.result_value = addr 97 98 def native_logical_op(self, op): 99 self.machine.LoadName(0) # left value 100 left_data = self.machine.value + self.instance_data_offset 101 self.machine.LoadName(1) # right value 102 right_data = self.machine.value + self.instance_data_offset 103 104 # Test the data. 105 # NOTE: The data is considered ready to use. 106 107 if op(self.machine.load(left_data), self.machine.load(right_data)): 108 self.machine.result_context = self.constants[True] 109 self.machine.result_value = self.constants[True] 110 else: 111 self.machine.result_context = self.constants[False] 112 self.machine.result_value = self.constants[False] 113 114 # Operators. 115 # Although this takes a short-cut by using the operator module, testing is 116 # still performed on the operands to ensure that they qualify for these 117 # native operations. 118 119 def native_int_add(self): 120 return self.native_int_arithmetic_op(operator.add) 121 122 def native_int_sub(self): 123 return self.native_int_arithmetic_op(operator.sub) 124 125 def native_int_pow(self): 126 return self.native_int_arithmetic_op(operator.pow) 127 128 def native_int_and(self): 129 return self.native_int_arithmetic_op(operator.and_) 130 131 def native_int_or(self): 132 return self.native_int_arithmetic_op(operator.or_) 133 134 def native_int_lt(self): 135 return self.native_logical_op(operator.lt) 136 137 def native_int_gt(self): 138 return self.native_logical_op(operator.gt) 139 140 def native_int_eq(self): 141 return self.native_logical_op(operator.eq) 142 143 def native_str_lt(self): 144 return self.native_logical_op(operator.lt) 145 146 def native_str_gt(self): 147 return self.native_logical_op(operator.gt) 148 149 def native_str_eq(self): 150 return self.native_logical_op(operator.eq) 151 152 # Specific operator methods. 153 154 def builtins_int_neg(self): 155 self.machine.LoadName(0) # left value 156 left_data = self.machine.value + self.instance_data_offset 157 158 # Make a new object. 159 160 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 161 162 # Store the result. 163 # NOTE: The data is considered ready to use. 164 165 self.machine.save(addr + self.instance_data_offset, -self.machine.load(left_data)) 166 167 # Return the new object. 168 # Introduce object as context for the new object. 169 170 self.machine.result_context = addr 171 self.machine.result_value = addr 172 173 # Various built-in methods. 174 175 def builtins_list_new(self): 176 frame = self.local_sp_stack[-1] 177 178 # The first parameter should be the instance. 179 180 list_value = self.frame_stack[frame] 181 182 # Make a new sequence. 183 # NOTE: Using an arbitrary size. 184 185 new_fragment = self.machine._MakeFragment(self.fragment_data_offset, 5) # include the header 186 187 # Complete the list instance by saving the fragment reference. 188 # NOTE: This requires an attribute in the list structure. 189 190 addr = list_value.ref + self.instance_data_offset 191 self.machine.save(addr, DataValue(None, new_fragment)) 192 193 def builtins_list_get_single_item(self): 194 frame = self.local_sp_stack[-1] 195 196 # Get the operand address. 197 198 item_value = self.frame_stack[frame + 1] 199 200 # Get the list address. 201 202 obj_value = self.frame_stack[frame] 203 204 # Get the fragment address. 205 206 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 207 208 # Get the fragment header. 209 210 header = self.machine.load(fragment.ref) 211 nelements = header.occupied_size - self.fragment_data_offset 212 213 # Get the item position. 214 215 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 216 217 if not self._check_index(item_pos, nelements): 218 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 219 return self.machine.RaiseException() 220 221 # Get the item itself. 222 223 data = self.machine.load(fragment.ref + self.fragment_data_offset + item_pos) 224 self.machine.result_context = data.context 225 self.machine.result_value = data.ref 226 227 def builtins_list_len(self): 228 frame = self.local_sp_stack[-1] 229 230 # Get the list address. 231 232 obj_value = self.frame_stack[frame] 233 234 # Get the fragment address. 235 236 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 237 238 # Get the fragment header. 239 240 header = self.machine.load(fragment.ref) 241 nelements = header.occupied_size - self.fragment_data_offset 242 243 # Make a new object. 244 245 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 246 247 # Store the result. 248 # NOTE: The data is considered ready to use. 249 250 self.machine.save(addr + self.instance_data_offset, nelements) 251 252 # Return the new object. 253 # Introduce object as context for the new object. 254 255 self.machine.result_context = addr 256 self.machine.result_value = addr 257 258 def builtins_list_append(self): 259 frame = self.local_sp_stack[-1] 260 261 # Get operand address. 262 263 arg_value = self.frame_stack[frame + 1] 264 265 # Get the list address. 266 267 obj_value = self.frame_stack[frame] 268 269 # Get the fragment address. 270 271 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 272 273 # Get the fragment header. 274 275 header = self.machine.load(fragment.ref) 276 277 # Attempt to add the reference. 278 279 if header.occupied_size < header.allocated_size: 280 self.machine.save(fragment.ref + header.occupied_size, arg_value) 281 header.occupied_size += 1 282 else: 283 284 # Make a new fragment, maintaining more space than currently 285 # occupied in order to avoid reallocation. 286 287 new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2) 288 289 # Copy existing elements. 290 291 for i in range(self.fragment_data_offset, header.occupied_size): 292 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i)) 293 294 self.machine.save(new_fragment + header.occupied_size, arg_value) 295 296 # Set the new fragment in the object. 297 # NOTE: The old fragment could be deallocated. 298 299 self.machine.save(obj_value.ref + self.instance_data_offset, DataValue(None, new_fragment)) 300 301 def builtins_tuple_new(self): 302 frame = self.local_sp_stack[-1] 303 304 # Get the sequence address. 305 # The first argument should be empty since this function acts as an 306 # instantiator, and in instantiators the first argument is reserved so 307 # that it can be filled in for the call to an initialiser without 308 # allocating a new frame. 309 310 obj_value = self.frame_stack[frame + 1] 311 return self._builtins_tuple(obj_value) 312 313 def builtins_tuple(self): 314 frame = self.local_sp_stack[-1] 315 316 # Get the sequence address. 317 318 obj_value = self.frame_stack[frame] 319 return self._builtins_tuple(obj_value) 320 321 def _builtins_tuple(self, obj_value): 322 323 if self.machine._CheckInstance(obj_value.ref, self.tuple_class): 324 self.machine.result_context = obj_value.context 325 self.machine.result_value = obj_value.ref 326 return 327 328 # Reject non-list, non-tuple types. 329 # NOTE: This should probably accept any sequence. 330 331 elif not self.machine._CheckInstance(obj_value.ref, self.list_class): 332 self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance) 333 return self.machine.RaiseException() 334 335 # Get the fragment address. 336 337 fragment = self.machine.load(obj_value.ref + self.instance_data_offset) 338 339 # Get the fragment header. 340 341 header = self.machine.load(fragment.ref) 342 343 # Make a new object. 344 345 addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance) 346 347 # Copy the fragment contents into the tuple. 348 # NOTE: This might be done by repurposing the fragment in some situations. 349 350 for i in range(self.fragment_data_offset, header.occupied_size): 351 self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i)) 352 353 # Return the new object. 354 # Introduce object as context for the new object. 355 356 self.machine.result_context = addr 357 self.machine.result_value = addr 358 359 def builtins_tuple_len(self): 360 frame = self.local_sp_stack[-1] 361 362 # Get the tuple address. 363 364 obj_value = self.frame_stack[frame] 365 366 # Get the header. 367 368 header = self.machine.load(obj_value.ref) 369 nelements = header.size - self.instance_data_offset 370 371 # Make a new object. 372 373 addr = self.machine._MakeObject(self.instance_size, self.int_instance) 374 375 # Store the result. 376 # NOTE: The data is considered ready to use. 377 378 self.machine.save(addr + self.instance_data_offset, nelements) 379 380 # Return the new object. 381 # Introduce object as context for the new object. 382 383 self.machine.result_context = addr 384 self.machine.result_value = addr 385 386 def builtins_tuple_get_single_item(self): 387 frame = self.local_sp_stack[-1] 388 389 # Get the operand address. 390 391 item_value = self.frame_stack[frame + 1] 392 393 # Get the tuple address. 394 395 obj_value = self.frame_stack[frame] 396 397 # Get the header. 398 399 header = self.machine.load(obj_value.ref) 400 nelements = header.size - self.instance_data_offset 401 402 # NOTE: Assume single location for data and header. 403 404 item_pos = self.machine.load(item_value.ref + self.instance_data_offset) 405 406 if not self._check_index(item_pos, nelements): 407 self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance) 408 return self.machine.RaiseException() 409 410 # Get the item. 411 412 data = self.machine.load(obj_value.ref + self.instance_data_offset + item_pos) 413 self.machine.result_context = data.context 414 self.machine.result_value = data.ref 415 416 def builtins_getattr(self): 417 frame = self.local_sp_stack[-1] 418 419 # Get the object, attribute name. 420 421 obj_value = self.frame_stack[frame] 422 name_value = self.frame_stack[frame + 1] 423 424 if not self.machine._CheckInstance(name_value.ref, self.accessor_class): 425 self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance) 426 return self.machine.RaiseException() 427 428 # Get the object table index from the name. It is a bare integer, not a reference. 429 430 index = self.machine.load(name_value.ref + self.instance_data_offset + 1) 431 432 # NOTE: This is very much like LoadAttrIndexContextCond. 433 434 data = self.machine.load(obj_value.ref) 435 element = self.machine.objlist[data.classcode + index] 436 437 if element is not None: 438 attr_index, static_attr, offset = element 439 if attr_index == index: 440 if static_attr: 441 loaded_data = self.machine.load(offset) # offset is address of class/module attribute 442 if data.attrcode is not None: # absent attrcode == class/module 443 loaded_data = self.machine._LoadAddressContextCond(loaded_data.context, loaded_data.ref, obj_value.ref) 444 else: 445 loaded_data = self.machine.load(obj_value.ref + offset) 446 self.machine.result_context = loaded_data.context 447 self.machine.result_value = loaded_data.ref 448 return 449 450 self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance) 451 return self.machine.RaiseException() 452 453 def builtins_isinstance(self): 454 frame = self.local_sp_stack[-1] 455 456 # Get the operand addresses. 457 458 obj_value = self.frame_stack[frame] 459 cls_value = self.frame_stack[frame + 1] 460 461 if self.machine._CheckInstance(obj_value.ref, cls_value.ref): 462 self.machine.result_context = self.constants[True] 463 self.machine.result_value = self.constants[True] 464 else: 465 self.machine.result_context = self.constants[False] 466 self.machine.result_value = self.constants[False] 467 468 def builtins_print(self): 469 # NOTE: Do nothing for now. 470 pass 471 472 def builtins_printnl(self): 473 # NOTE: Do nothing for now. 474 pass 475 476 native_functions = { 477 478 # Native method implementations: 479 480 "native._int_add" : native_int_add, 481 "native._int_sub" : native_int_sub, 482 "native._int_pow" : native_int_pow, 483 "native._int_and" : native_int_and, 484 "native._int_or" : native_int_or, 485 "native._int_lt" : native_int_lt, 486 "native._int_gt" : native_int_gt, 487 "native._int_eq" : native_int_eq, 488 "native._str_lt" : native_str_lt, 489 "native._str_gt" : native_str_gt, 490 "native._str_eq" : native_str_eq, 491 "__builtins__.int.__neg__" : builtins_int_neg, 492 "__builtins__.list.__get_single_item__" : builtins_list_get_single_item, 493 "__builtins__.list.__len__" : builtins_list_len, 494 "__builtins__.list.append" : builtins_list_append, 495 "__builtins__.tuple" : builtins_tuple_new, 496 "__builtins__.tuple.__len__" : builtins_tuple_len, 497 "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item, 498 499 # Native initialisers: 500 501 "__builtins__.BaseException.__init__" : builtins_no_op, # NOTE: To be made distinct, potentially in the builtins module. 502 503 # Native functions: 504 505 "__builtins__._getattr" : builtins_getattr, 506 507 # Native instantiator helpers: 508 509 "__builtins__.list.__new__" : builtins_list_new, 510 511 # Native helper functions: 512 513 "__builtins__._isinstance" : builtins_isinstance, 514 "__builtins__._print" : builtins_print, 515 "__builtins__._printnl" : builtins_printnl, 516 "__builtins__._tuple" : builtins_tuple, 517 } 518 519 # vim: tabstop=4 expandtab shiftwidth=4