micropython

rsvplib.py

431:dced8cb98f4a
2011-06-13 Paul Boddie Introduced a native functions module and a generic binary operator wrapper function for primitive types, reducing the hand-written code in the RSVP library, which now uses actual RSVP instructions in some places as opposed to altering the machine's state directly. Moved various __bool__ method implementations back into the __builtins__ module.
     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