micropython

rsvplib.py

414:ea8cd6ca4083
2011-05-08 Paul Boddie Fixed the placeholder default for getattr which was a hybrid of the old and new mechanisms for defining such a placeholder.
     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(self):   380         frame = self.local_sp_stack[-1]   381    382         # Get the list address.   383    384         obj_value = self.frame_stack[frame]   385    386         if not self.machine._CheckInstance(obj_value.ref, self.list_class):   387             self.machine.exception = self.machine._MakeObject(self.instance_size, self.type_error_instance)   388             return self.machine.RaiseException()   389    390         # Get the fragment address.   391    392         fragment = self.machine.load(obj_value.ref + self.instance_data_offset)   393    394         # Get the fragment header.   395    396         header = self.machine.load(fragment.ref)   397    398         # Make a new object.   399    400         addr = self.machine._MakeObject(self.instance_data_offset + header.occupied_size - self.fragment_data_offset, self.tuple_instance)   401    402         # Copy the fragment contents into the tuple.   403         # NOTE: This might be done by repurposing the fragment in some situations.   404    405         for i in range(self.fragment_data_offset, header.occupied_size):   406             self.machine.save(addr + self.instance_data_offset + i - self.fragment_data_offset, self.machine.load(fragment.ref + i))   407    408         # Return the new object.   409         # Introduce object as context for the new object.   410    411         self.machine.result = DataValue(addr, addr)   412    413     def builtins_tuple_len(self):   414         frame = self.local_sp_stack[-1]   415    416         # Get the tuple address.   417    418         obj_value = self.frame_stack[frame]   419    420         # Get the header.   421    422         header = self.machine.load(obj_value.ref)   423         nelements = header.size - self.instance_data_offset   424    425         # Make a new object.   426    427         addr = self.machine._MakeObject(self.instance_size, self.int_instance)   428    429         # Store the result.   430         # NOTE: The data is considered ready to use.   431    432         self.machine.save(addr + self.instance_data_offset, nelements)   433    434         # Return the new object.   435         # Introduce object as context for the new object.   436    437         self.machine.result = DataValue(addr, addr)   438    439     def builtins_tuple_get_single_item(self):   440         frame = self.local_sp_stack[-1]   441    442         # Get the operand address.   443    444         item_value = self.frame_stack[frame + 1]   445    446         # Get the tuple address.   447    448         obj_value = self.frame_stack[frame]   449    450         # Get the header.   451    452         header = self.machine.load(obj_value.ref)   453         nelements = header.size - self.instance_data_offset   454    455         # NOTE: Assume single location for data and header.   456    457         item_pos = self.machine.load(item_value.ref + self.instance_data_offset)   458    459         if not self._check_index(item_pos, nelements):   460             self.machine.exception = self.machine._MakeObject(self.instance_size, self.index_error_instance)   461             return self.machine.RaiseException()   462    463         # Get the item.   464    465         self.machine.result = self.machine.load(obj_value.ref + self.instance_data_offset + item_pos)   466    467     def builtins_object_init(self):   468         pass   469    470     def builtins_getattr(self):   471         frame = self.local_sp_stack[-1]   472    473         # Get the object, attribute name.   474    475         obj_value = self.frame_stack[frame]   476         name_value = self.frame_stack[frame + 1]   477    478         if not self.machine._CheckInstance(name_value.ref, self.accessor_class):   479             self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance)   480             return self.machine.RaiseException()   481    482         # Get the object table index from the name. It is a bare integer, not a reference.   483    484         index = self.machine.load(name_value.ref + self.instance_data_offset + 1)   485    486         # NOTE: This is very much like LoadAttrIndex.   487    488         data = self.machine.load(obj_value.ref)   489         element = self.machine.objlist[data.classcode + index]   490    491         if element is not None:   492             attr_index, static_attr, offset = element   493             if attr_index == index:   494                 if static_attr:   495                     self.machine.result = self.machine.load(offset) # offset is address of class/module attribute   496                 else:   497                     self.machine.result = self.machine.load(obj_value.ref + offset)   498                 return   499    500         self.machine.exception = self.machine._MakeObject(self.instance_size, self.attr_error_instance)   501         return self.machine.RaiseException()   502    503     def builtins_isinstance(self):   504         frame = self.local_sp_stack[-1]   505    506         # Get the operand addresses.   507    508         obj_value = self.frame_stack[frame]   509         cls_value = self.frame_stack[frame + 1]   510    511         if self.machine._CheckInstance(obj_value.ref, cls_value.ref):   512             self.machine.result = DataValue(self.constants[True], self.constants[True])   513         else:   514             self.machine.result = DataValue(self.constants[False], self.constants[False])   515    516     def builtins_print(self):   517         # NOTE: Do nothing for now.   518         pass   519    520     def builtins_printnl(self):   521         # NOTE: Do nothing for now.   522         pass   523    524     native_functions = {   525    526         # Native method implementations:   527    528         "__builtins__.int.__add__" : builtins_int_add,   529         "__builtins__.int.__radd__" : builtins_int_add,               # NOTE: To be made distinct.   530         "__builtins__.int.__sub__" : builtins_int_sub,   531         "__builtins__.int.__pow__" : builtins_int_pow,   532         "__builtins__.int.__iadd__" : builtins_int_add,   533         "__builtins__.int.__bool__" : builtins_int_bool,   534         "__builtins__.int.__neg__" : builtins_int_neg,   535         "__builtins__.int.__lt__" : builtins_int_lt,   536         "__builtins__.int.__le__" : builtins_int_le,   537         "__builtins__.int.__gt__" : builtins_int_gt,   538         "__builtins__.int.__ge__" : builtins_int_ge,   539         "__builtins__.int.__eq__" : builtins_int_eq,   540         "__builtins__.int.__ne__" : builtins_int_ne,   541         "__builtins__.int.__and__" : builtins_int_and,   542         "__builtins__.int.__rand__" : builtins_int_and,   543         "__builtins__.int.__or__" : builtins_int_or,   544         "__builtins__.int.__ror__" : builtins_int_or,   545         "__builtins__.bool.__bool__" : builtins_bool_bool,   546         "__builtins__.list.__get_single_item__" : builtins_list_get_single_item,   547         "__builtins__.list.__len__" : builtins_list_len,   548         "__builtins__.list.append" : builtins_list_append,   549         "__builtins__.tuple.__len__" : builtins_tuple_len,   550         "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item,   551         "__builtins__.basestring.__lt__" : builtins_str_lt,   552         "__builtins__.basestring.__le__" : builtins_str_le,   553         "__builtins__.basestring.__gt__" : builtins_str_gt,   554         "__builtins__.basestring.__ge__" : builtins_str_ge,   555         "__builtins__.basestring.__eq__" : builtins_str_eq,   556         "__builtins__.basestring.__ne__" : builtins_str_ne,   557    558         # Native initialisers:   559    560         "__builtins__.object.__init__" : builtins_object_init,        # NOTE: A no-operation.   561         "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module.   562    563         # Native functions:   564    565         "__builtins__._getattr" : builtins_getattr,   566    567         # Native instantiator helpers:   568    569         "__builtins__.list.__new__" : builtins_list_new,   570    571         # Native helper functions:   572    573         "__builtins__._isinstance" : builtins_isinstance,   574         "__builtins__._print" : builtins_print,   575         "__builtins__._printnl" : builtins_printnl,   576         "__builtins__._tuple" : builtins_tuple,   577         }   578    579 # vim: tabstop=4 expandtab shiftwidth=4