micropython

rsvplib.py

402:c87bdb84d9d3
2011-02-27 Paul Boddie Introduced placeholder functions for print in order to let all tests pass. Added TestIdentity and status-related optimisation suggestions.
     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     34     def __init__(self, machine, constants):    35     36         """    37         Initialise the library with the 'machine' and the 'constants' addresses    38         dictionary.    39         """    40     41         self.machine = machine    42         self.constants = constants    43     44         # Native class constants.    45     46         cls = self.machine._get_class("__builtins__", "int")    47         self.int_class = cls.location    48         self.int_instance = cls.instance_template_location    49         cls = self.machine._get_class("__builtins__", "list")    50         if cls is not None:    51             self.list_class = cls.location    52             self.list_instance = cls.instance_template_location    53         cls = self.machine._get_class("__builtins__", "IndexError")    54         if cls is not None:    55             self.index_error = cls.location    56             self.index_error_instance = cls.instance_template_location    57         cls = self.machine._get_class("__builtins__", "basestring")    58         if cls is not None:    59             self.str_class = cls.location    60             self.str_instance = cls.instance_template_location    61     62         self.tuple_class = self.machine.tuple_class    63         self.type_error_instance = self.machine.type_error_instance    64     65         self.frame_stack = self.machine.frame_stack    66         self.local_sp_stack = self.machine.local_sp_stack    67     68     def _check_index(self, pos, nelements):    69         return pos >= 0 and pos < nelements    70     71     def builtins_int_arithmetic_op(self, op):    72         frame = self.local_sp_stack[-1]    73     74         # Get operands addresses.    75     76         left_value = self.frame_stack[frame]    77         right_value = self.frame_stack[frame + 1]    78     79         # Test operand suitability.    80         # NOTE: Support other types.    81     82         if not (self.machine._CheckInstance(left_value.ref, self.int_class) and    83             self.machine._CheckInstance(right_value.ref, self.int_class)):    84     85             self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance)    86             return self.machine.RaiseException()    87     88         left_data = left_value.ref + self.instance_data_offset    89         right_data = right_value.ref + self.instance_data_offset    90     91         # Make a new object.    92     93         addr = self.machine._MakeObject(self.instance_size, self.int_instance)    94     95         # Store the result.    96         # NOTE: The data is considered ready to use.    97     98         self.machine.save(addr + self.instance_data_offset, op(self.machine.load(left_data), self.machine.load(right_data)))    99    100         # Return the new object.   101         # Introduce object as context for the new object.   102    103         self.machine.result = DataValue(addr, addr)   104    105     def builtins_logical_op(self, operand_class, op):   106         frame = self.local_sp_stack[-1]   107    108         # Get operands addresses.   109    110         left_value = self.frame_stack[frame]   111         right_value = self.frame_stack[frame + 1]   112    113         # Test operand suitability.   114         # NOTE: Handle comparisons of incompatible types more appropriately.   115         # NOTE: Return NotImplemented.   116    117         if not (self.machine._CheckInstance(left_value.ref, operand_class) and   118             self.machine._CheckInstance(right_value.ref, operand_class)):   119    120             notimpl = self.constants[NotImplemented]   121             self.machine.result = DataValue(notimpl, notimpl)   122             return   123    124         left_data = left_value.ref + self.instance_data_offset   125         right_data = right_value.ref + self.instance_data_offset   126    127         # Test the data.   128         # NOTE: The data is considered ready to use.   129    130         if op(self.machine.load(left_data), self.machine.load(right_data)):   131             self.machine.result = DataValue(self.constants[True], self.constants[True])   132         else:   133             self.machine.result = DataValue(self.constants[False], self.constants[False])   134    135     # Operators.   136     # Although this takes a short-cut by using the operator module, testing is   137     # still performed on the operands to ensure that they qualify for these   138     # native operations.   139    140     def builtins_int_add(self):   141         return self.builtins_int_arithmetic_op(operator.add)   142    143     def builtins_int_sub(self):   144         return self.builtins_int_arithmetic_op(operator.sub)   145    146     def builtins_int_pow(self):   147         return self.builtins_int_arithmetic_op(operator.pow)   148    149     def builtins_int_lt(self):   150         return self.builtins_logical_op(self.int_class, operator.lt)   151    152     def builtins_int_le(self):   153         return self.builtins_logical_op(self.int_class, operator.le)   154    155     def builtins_int_gt(self):   156         return self.builtins_logical_op(self.int_class, operator.gt)   157    158     def builtins_int_ge(self):   159         return self.builtins_logical_op(self.int_class, operator.ge)   160    161     def builtins_int_eq(self):   162         return self.builtins_logical_op(self.int_class, operator.eq)   163    164     def builtins_int_ne(self):   165         return self.builtins_logical_op(self.int_class, operator.ne)   166    167     def builtins_str_lt(self):   168         return self.builtins_logical_op(self.str_class, operator.lt)   169    170     def builtins_str_le(self):   171         return self.builtins_logical_op(self.str_class, operator.le)   172    173     def builtins_str_gt(self):   174         return self.builtins_logical_op(self.str_class, operator.gt)   175    176     def builtins_str_ge(self):   177         return self.builtins_logical_op(self.str_class, operator.ge)   178    179     def builtins_str_eq(self):   180         return self.builtins_logical_op(self.str_class, operator.eq)   181    182     def builtins_str_ne(self):   183         return self.builtins_logical_op(self.str_class, operator.ne)   184    185     def builtins_int_and(self):   186         return self.builtins_int_arithmetic_op(operator.and_)   187    188     def builtins_int_or(self):   189         return self.builtins_int_arithmetic_op(operator.or_)   190    191     # Specific operator methods.   192    193     def builtins_int_bool(self):   194         frame = self.local_sp_stack[-1]   195    196         # Get operands addresses.   197    198         left_value = self.frame_stack[frame]   199    200         # Test operand suitability.   201    202         if not self.machine._CheckInstance(left_value.ref, self.int_class):   203             self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance)   204             return self.machine.RaiseException()   205    206         left_data = left_value.ref + self.instance_data_offset   207    208         # Test the data.   209         # NOTE: The data is considered ready to use.   210    211         if self.machine.load(left_data) != 0:   212             self.machine.result = DataValue(self.constants[True], self.constants[True])   213         else:   214             self.machine.result = DataValue(self.constants[False], self.constants[False])   215    216     def builtins_int_neg(self):   217         frame = self.local_sp_stack[-1]   218    219         # Get operands addresses.   220    221         left_value = self.frame_stack[frame]   222    223         # Test operand suitability.   224    225         if not self.machine._CheckInstance(left_value.ref, self.int_class):   226             self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance)   227             return self.machine.RaiseException()   228    229         left_data = left_value.ref + self.instance_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, -self.machine.load(left_data))   239    240         # Return the new object.   241         # Introduce object as context for the new object.   242    243         self.machine.result = DataValue(addr, addr)   244    245     # Various built-in methods.   246    247     def builtins_bool_bool(self):   248         frame = self.local_sp_stack[-1]   249    250         # Get operands addresses.   251    252         left_value = self.frame_stack[frame]   253         self.machine.result = DataValue(left_value.ref, left_value.ref)   254    255     def builtins_list_new(self):   256         frame = self.local_sp_stack[-1]   257    258         # The first parameter should be the instance.   259    260         list_value = self.frame_stack[frame]   261    262         # Make a new sequence.   263         # NOTE: Using an arbitrary size.   264    265         new_fragment = self.machine._MakeFragment(1, 5) # include the header   266    267         # Complete the list instance by saving the fragment reference.   268         # NOTE: This requires an attribute in the list structure.   269    270         addr = list_value.ref + self.instance_data_offset   271         self.machine.save(addr, DataValue(None, new_fragment))   272    273     def builtins_list_get_single_item(self):   274         frame = self.local_sp_stack[-1]   275    276         # Get the operand address.   277    278         item_value = self.frame_stack[frame + 1]   279    280         # Get the list address.   281    282         obj_value = self.frame_stack[frame]   283    284         # Get the fragment address.   285    286         fragment = self.machine.load(obj_value.ref + self.instance_data_offset)   287    288         # Get the fragment header.   289    290         header = self.machine.load(fragment.ref)   291         nelements = header.occupied_size - 1   292    293         # Get the item position.   294    295         item_pos = self.machine.load(item_value.ref + self.instance_data_offset)   296    297         if not self._check_index(item_pos, nelements):   298             self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance)   299             return self.machine.RaiseException()   300    301         # Get the item itself.   302    303         self.machine.result = self.machine.load(fragment.ref + 1 + item_pos)   304    305     def builtins_list_len(self):   306         frame = self.local_sp_stack[-1]   307    308         # Get the list address.   309    310         obj_value = self.frame_stack[frame]   311    312         # Get the fragment address.   313    314         fragment = self.machine.load(obj_value.ref + self.instance_data_offset)   315    316         # Get the fragment header.   317    318         header = self.machine.load(fragment.ref)   319         nelements = header.occupied_size - 1   320    321         # Make a new object.   322    323         addr = self.machine._MakeObject(self.instance_size, self.int_instance)   324    325         # Store the result.   326         # NOTE: The data is considered ready to use.   327    328         self.machine.save(addr + self.instance_data_offset, nelements)   329    330         # Return the new object.   331         # Introduce object as context for the new object.   332    333         self.machine.result = DataValue(addr, addr)   334    335     def builtins_list_append(self):   336         frame = self.local_sp_stack[-1]   337    338         # Get operand address.   339    340         arg_value = self.frame_stack[frame + 1]   341    342         # Get the list address.   343    344         obj_value = self.frame_stack[frame]   345    346         # Get the fragment address.   347    348         fragment = self.machine.load(obj_value.ref + self.instance_data_offset)   349    350         # Get the fragment header.   351    352         header = self.machine.load(fragment.ref)   353    354         # Attempt to add the reference.   355    356         if header.occupied_size < header.allocated_size:   357             self.machine.save(fragment.ref + header.occupied_size, arg_value)   358             header.occupied_size += 1   359         else:   360    361             # Make a new fragment, maintaining more space than currently   362             # occupied in order to avoid reallocation.   363    364             new_fragment = self.machine._MakeFragment(header.occupied_size + 1, header.occupied_size * 2)   365    366             # Copy existing elements.   367    368             for i in range(1, header.occupied_size):   369                 self.machine.save(new_fragment + i, self.machine.load(fragment.ref + i))   370    371             self.machine.save(new_fragment + header.occupied_size, arg_value)   372    373             # Set the new fragment in the object.   374             # NOTE: The old fragment could be deallocated.   375    376             self.machine.save(obj_value.ref + self.instance_data_offset, DataValue(None, new_fragment))   377    378     def builtins_tuple_len(self):   379         frame = self.local_sp_stack[-1]   380    381         # Get the tuple address.   382    383         obj_value = self.frame_stack[frame]   384    385         # Get the header.   386    387         header = self.machine.load(obj_value.ref)   388         nelements = header.size - self.instance_data_offset   389    390         # Make a new object.   391    392         addr = self.machine._MakeObject(self.instance_size, self.int_instance)   393    394         # Store the result.   395         # NOTE: The data is considered ready to use.   396    397         self.machine.save(addr + self.instance_data_offset, nelements)   398    399         # Return the new object.   400         # Introduce object as context for the new object.   401    402         self.machine.result = DataValue(addr, addr)   403    404     def builtins_tuple_get_single_item(self):   405         frame = self.local_sp_stack[-1]   406    407         # Get the operand address.   408    409         item_value = self.frame_stack[frame + 1]   410    411         # Get the tuple address.   412    413         obj_value = self.frame_stack[frame]   414    415         # Get the header.   416    417         header = self.machine.load(obj_value.ref)   418         nelements = header.size - self.instance_data_offset   419    420         # NOTE: Assume single location for data and header.   421    422         item_pos = self.machine.load(item_value.ref + self.instance_data_offset)   423    424         if not self._check_index(item_pos, nelements):   425             self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance)   426             return self.machine.RaiseException()   427    428         # Get the item.   429    430         self.machine.result = self.machine.load(obj_value.ref + self.instance_data_offset + item_pos)   431    432     def builtins_object_init(self):   433         pass   434    435     def builtins_isinstance(self):   436         frame = self.local_sp_stack[-1]   437    438         # Get the operand addresses.   439    440         obj_value = self.frame_stack[frame]   441         cls_value = self.frame_stack[frame + 1]   442    443         if self.machine._CheckInstance(obj_value.ref, cls_value.ref):   444             self.machine.result = DataValue(self.constants[True], self.constants[True])   445         else:   446             self.machine.result = DataValue(self.constants[False], self.constants[False])   447    448     def builtins_print(self):   449         # NOTE: Do nothing for now.   450         pass   451    452     def builtins_printnl(self):   453         # NOTE: Do nothing for now.   454         pass   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         "__builtins__._print" : builtins_print,   503         "__builtins__._printnl" : builtins_printnl,   504         }   505    506 # vim: tabstop=4 expandtab shiftwidth=4