micropython

rsvplib.py

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