micropython

rsvplib.py

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