micropython

rsvplib.py

541:ad2a6b7ef7bb
2012-06-11 Paul Boddie Introduced an explicit test for non-class base class references.
     1 #!/usr/bin/env python     2      3 """     4 A native function library for a really simple virtual processor.     5 NOTE: Ultimately, this should only implement only operations at the lowest     6 NOTE: possible level, with generated native methods providing the functionality     7 NOTE: instead.     8      9 Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk>    10     11 This program is free software; you can redistribute it and/or modify it under    12 the terms of the GNU General Public License as published by the Free Software    13 Foundation; either version 3 of the License, or (at your option) any later    14 version.    15     16 This program is distributed in the hope that it will be useful, but WITHOUT    17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    18 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    19 details.    20     21 You should have received a copy of the GNU General Public License along with    22 this program.  If not, see <http://www.gnu.org/licenses/>.    23 """    24     25 from micropython.program import DataValue    26 import operator    27     28 class Library:    29     30     "Native function implementations."    31     32     # NOTE: These attributes need changing if the instance layout changes.    33     34     instance_template_size = instance_data_offset = 1    35     instance_size = instance_template_size + 1    36     fragment_data_offset = 1    37     38     def __init__(self, machine, constants):    39     40         """    41         Initialise the library with the 'machine' and the 'constants' addresses    42         dictionary.    43         """    44     45         self.machine = machine    46         self.constants = constants    47     48         # Native class constants.    49     50         self.int_class, self.int_instance = self._get_builtin_class_and_template("int")    51         self.list_class, self.list_instance = self._get_builtin_class_and_template("list")    52         self.index_error, self.index_error_instance = self._get_builtin_class_and_template("IndexError")    53         self.str_class, self.str_instance = self._get_builtin_class_and_template("basestring")    54         self.accessor_class, self.accessor_instance = self._get_builtin_class_and_template("_accessor")    55     56         self.tuple_class = self.machine.tuple_class    57         self.tuple_instance = self.machine.tuple_instance    58     59         self.attr_error_instance = self.machine.attr_error_instance    60         self.type_error_instance = self.machine.type_error_instance    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         left = self.machine.load_from_frame(0)    79         left_data = left.ref + self.instance_data_offset    80         right = self.machine.load_from_frame(1)    81         right_data = right.ref + 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.LoadImmediate(addr, "working_context")    96         self.machine.LoadImmediate(addr, "working")    97     98     def native_logical_op(self, op):    99         left = self.machine.load_from_frame(0)   100         left_data = left.ref + self.instance_data_offset   101         right = self.machine.load_from_frame(1)   102         right_data = right.ref + 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.LoadImmediate(self.constants[True], "working_context")   109             self.machine.LoadImmediate(self.constants[True], "working")   110         else:   111             self.machine.LoadImmediate(self.constants[False], "working_context")   112             self.machine.LoadImmediate(self.constants[False], "working")   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         left = self.machine.load_from_frame(0)   156         left_data = left.ref + 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.LoadImmediate(addr, "working_context")   171         self.machine.LoadImmediate(addr, "working")   172    173     # Various built-in methods.   174    175     def builtins_list_new(self):   176         list_value = self.machine.load_from_frame(0)   177         list_addr = list_value.ref + self.instance_data_offset   178    179         # Make a new sequence.   180         # NOTE: Using an arbitrary size.   181    182         new_fragment = self.machine._MakeFragment(self.fragment_data_offset, 5) # include the header   183    184         # Complete the list instance by saving the fragment reference.   185         # NOTE: This requires an attribute in the list structure.   186    187         self.machine.save(list_addr, DataValue(None, new_fragment))   188    189     def builtins_list_get_single_item(self):   190         obj_value = self.machine.load_from_frame(0)   191         fragment_member = obj_value.ref + self.instance_data_offset   192         item_value = self.machine.load_from_frame(1)   193         item_pos_member = item_value.ref + self.instance_data_offset   194    195         # Get the fragment address.   196    197         fragment = self.machine.load(fragment_member)   198    199         # Get the fragment header.   200    201         header = self.machine.load(fragment.ref)   202         nelements = header.occupied_size - self.fragment_data_offset   203    204         # Get the item position.   205    206         item_pos = self.machine.load(item_pos_member)   207    208         if not self._check_index(item_pos, nelements):   209             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.index_error_instance), "exception")   210             return self.machine.RaiseException()   211    212         # Get the item itself.   213    214         data = self.machine.load(fragment.ref + self.fragment_data_offset + item_pos)   215         self.machine.LoadImmediate(data.context, "working_context")   216         self.machine.LoadImmediate(data.ref, "working")   217    218     def builtins_list_len(self):   219         obj_value = self.machine.load_from_frame(0)   220         fragment_member = obj_value.ref + self.instance_data_offset   221    222         # Get the fragment address.   223    224         fragment = self.machine.load(fragment_member)   225    226         # Get the fragment header.   227    228         header = self.machine.load(fragment.ref)   229         nelements = header.occupied_size - self.fragment_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, nelements)   239    240         # Return the new object.   241         # Introduce object as context for the new object.   242    243         self.machine.LoadImmediate(addr, "working_context")   244         self.machine.LoadImmediate(addr, "working")   245    246     def builtins_list_append(self):   247         obj_value = self.machine.load_from_frame(0)   248         fragment_member = obj_value.ref + self.instance_data_offset   249         arg_value = self.machine.load_from_frame(1)   250    251         # Get the fragment address.   252    253         fragment = self.machine.load(fragment_member)   254    255         # Get the fragment header.   256    257         header = self.machine.load(fragment.ref)   258    259         # Attempt to add the reference.   260    261         if header.occupied_size < header.allocated_size:   262             self.machine.save(fragment.ref + header.occupied_size, arg_value)   263             header.occupied_size += 1   264         else:   265    266             # Make a new fragment, maintaining more space than currently   267             # occupied in order to avoid reallocation.   268    269             new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2)   270    271             # Copy existing elements.   272    273             for i in range(self.fragment_data_offset, header.occupied_size):   274                 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i))   275    276             self.machine.save(new_fragment + header.occupied_size, arg_value)   277    278             # Set the new fragment in the object.   279             # NOTE: The old fragment could be deallocated.   280    281             self.machine.save(fragment_member, DataValue(None, new_fragment))   282    283     def builtins_tuple_new(self):   284    285         # Get the sequence address.   286         # The first argument should be empty since this function acts as an   287         # instantiator, and in instantiators the first argument is reserved so   288         # that it can be filled in for the call to an initialiser without   289         # allocating a new frame.   290    291         obj_value = self.machine.load_from_frame(1)   292         return self._builtins_tuple(obj_value)   293    294     def builtins_tuple(self):   295    296         # Get the sequence address.   297    298         obj_value = self.machine.load_from_frame(0)   299         return self._builtins_tuple(obj_value)   300    301     def _builtins_tuple(self, obj_value):   302         fragment_member = obj_value.ref + self.instance_data_offset   303    304         if self.machine._CheckInstance(obj_value.ref, self.tuple_class):   305             self.machine.LoadImmediate(obj_value.context, "working_context")   306             self.machine.LoadImmediate(obj_value.ref, "working")   307             return   308    309         # Reject non-list, non-tuple types.   310         # NOTE: This should probably accept any sequence.   311    312         elif not self.machine._CheckInstance(obj_value.ref, self.list_class):   313             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.type_error_instance), "exception")   314             return self.machine.RaiseException()   315    316         # Get the fragment address.   317    318         fragment = self.machine.load(fragment_member)   319    320         # Get the fragment header.   321    322         header = self.machine.load(fragment.ref)   323    324         # Make a new object.   325    326         addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance)   327    328         # Copy the fragment contents into the tuple.   329         # NOTE: This might be done by repurposing the fragment in some situations.   330    331         for i in range(self.fragment_data_offset, header.occupied_size):   332             self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i))   333    334         # Return the new object.   335         # Introduce object as context for the new object.   336    337         self.machine.LoadImmediate(addr, "working_context")   338         self.machine.LoadImmediate(addr, "working")   339    340     def builtins_tuple_len(self):   341         obj_value = self.machine.load_from_frame(0)   342    343         # Get the header.   344    345         header = self.machine.load(obj_value.ref)   346         nelements = header.size - self.instance_data_offset   347    348         # Make a new object.   349    350         addr = self.machine._MakeObject(self.instance_size, self.int_instance)   351    352         # Store the result.   353         # NOTE: The data is considered ready to use.   354    355         self.machine.save(addr + self.instance_data_offset, nelements)   356    357         # Return the new object.   358         # Introduce object as context for the new object.   359    360         self.machine.LoadImmediate(addr, "working_context")   361         self.machine.LoadImmediate(addr, "working")   362    363     def builtins_tuple_get_single_item(self):   364         obj_value = self.machine.load_from_frame(0)   365         fragment_member = obj_value.ref + self.instance_data_offset   366         item_value = self.machine.load_from_frame(1)   367         item_pos_member = item_value.ref + self.instance_data_offset   368    369         # Get the header.   370    371         header = self.machine.load(obj_value.ref)   372         nelements = header.size - self.instance_data_offset   373    374         # NOTE: Assume single location for data and header.   375    376         item_pos = self.machine.load(item_pos_member)   377    378         if not self._check_index(item_pos, nelements):   379             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.index_error_instance), "exception")   380             return self.machine.RaiseException()   381    382         # Get the item.   383    384         data = self.machine.load(fragment_member + item_pos)   385         self.machine.LoadImmediate(data.context, "working_context")   386         self.machine.LoadImmediate(data.ref, "working")   387    388     def builtins_getattr(self):   389         obj_value = self.machine.load_from_frame(0)   390         name_value = self.machine.load_from_frame(1)   391         index_member = name_value.ref + self.instance_data_offset + 1   392    393         if not self.machine._CheckInstance(name_value.ref, self.accessor_class):   394             self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.attr_error_instance), "exception")   395             return self.machine.RaiseException()   396    397         # Get the object table index from the name. It is a bare integer, not a reference.   398    399         index = self.machine.load(index_member)   400    401         # NOTE: This is very much like LoadAttrIndexContextCond.   402    403         data = self.machine.load(obj_value.ref)   404         element = self.machine.load(self.machine.registers["objlist"] + data.classcode + index)   405    406         if element is not None:   407             attr_index, static_attr, offset = element   408             if attr_index == index:   409                 if static_attr:   410                     loaded_data = self.machine.load(offset) # offset is address of class/module attribute   411                     if data.attrcode is not None: # absent attrcode == class/module   412                         loaded_data = self.machine._LoadAddressContextCond(loaded_data.context, loaded_data.ref, obj_value.ref)   413                 else:   414                     loaded_data = self.machine.load(obj_value.ref + offset)   415                 self.machine.LoadImmediate(loaded_data.context, "working_context")   416                 self.machine.LoadImmediate(loaded_data.ref, "working")   417                 return   418    419         self.machine.LoadImmediate(self.machine._MakeObject(self.instance_size, self.attr_error_instance), "exception")   420         return self.machine.RaiseException()   421    422     def builtins_isinstance(self):   423         obj_value = self.machine.load_from_frame(0)   424         cls_value = self.machine.load_from_frame(1)   425    426         if self.machine._CheckInstance(obj_value.ref, cls_value.ref):   427             self.machine.LoadImmediate(self.constants[True], "working_context")   428             self.machine.LoadImmediate(self.constants[True], "working")   429         else:   430             self.machine.LoadImmediate(self.constants[False], "working_context")   431             self.machine.LoadImmediate(self.constants[False], "working")   432    433     def builtins_print(self):   434         # NOTE: Do nothing for now.   435         pass   436    437     def builtins_printnl(self):   438         # NOTE: Do nothing for now.   439         pass   440    441     native_functions = {   442    443         # Native method implementations:   444    445         "native._int_add"                           : native_int_add,   446         "native._int_sub"                           : native_int_sub,   447         "native._int_pow"                           : native_int_pow,   448         "native._int_and"                           : native_int_and,   449         "native._int_or"                            : native_int_or,   450         "native._int_lt"                            : native_int_lt,   451         "native._int_gt"                            : native_int_gt,   452         "native._int_eq"                            : native_int_eq,   453         "native._str_lt"                            : native_str_lt,   454         "native._str_gt"                            : native_str_gt,   455         "native._str_eq"                            : native_str_eq,   456         "__builtins__.int.__neg__"                  : builtins_int_neg,   457         "__builtins__.list.__get_single_item__"     : builtins_list_get_single_item,   458         "__builtins__.list.__len__"                 : builtins_list_len,   459         "__builtins__.list.append"                  : builtins_list_append,   460         "__builtins__.tuple"                        : builtins_tuple_new,   461         "__builtins__.tuple.__len__"                : builtins_tuple_len,   462         "__builtins__.tuple.__get_single_item__"    : builtins_tuple_get_single_item,   463    464         # Native initialisers:   465    466         "__builtins__.BaseException.__init__"       : builtins_no_op, # NOTE: To be made distinct, potentially in the builtins module.   467    468         # Native functions:   469    470         "__builtins__._getattr"                     : builtins_getattr,   471    472         # Native instantiator helpers:   473    474         "__builtins__.list.__new__"                 : builtins_list_new,   475    476         # Native helper functions:   477    478         "__builtins__._isinstance"                  : builtins_isinstance,   479         "__builtins__._print"                       : builtins_print,   480         "__builtins__._printnl"                     : builtins_printnl,   481         "__builtins__._tuple"                       : builtins_tuple,   482         }   483    484 # vim: tabstop=4 expandtab shiftwidth=4